From e07de5c4fba339ca1c3ca11cc6f582ee2c2bb0cd Mon Sep 17 00:00:00 2001 From: Zixuan Chen Date: Mon, 19 May 2025 11:38:23 +0800 Subject: [PATCH 1/8] feat: add redact to wasm - Implemented `redact_json_updates` function to redact sensitive content in JSON updates. - Added tests for `redactJsonUpdates` to ensure sensitive content is properly redacted. --- .changeset/grumpy-chicken-beam.md | 5 ++ crates/loro-ffi/src/doc.rs | 32 +++++++- crates/loro-internal/Cargo.toml | 2 +- crates/loro-internal/VERSION | 2 +- crates/loro-wasm/deno.lock | 3 +- crates/loro-wasm/package.json | 1 + crates/loro-wasm/scripts/build.ts | 6 ++ crates/loro-wasm/src/convert.rs | 33 ++++++++- crates/loro-wasm/src/lib.rs | 105 ++++++++++++++++++++------- crates/loro-wasm/tests/basic.test.ts | 51 +++++++++++++ pnpm-lock.yaml | 48 ++++++------ 11 files changed, 228 insertions(+), 60 deletions(-) create mode 100644 .changeset/grumpy-chicken-beam.md diff --git a/.changeset/grumpy-chicken-beam.md b/.changeset/grumpy-chicken-beam.md new file mode 100644 index 000000000..29936f252 --- /dev/null +++ b/.changeset/grumpy-chicken-beam.md @@ -0,0 +1,5 @@ +--- +"loro-crdt": patch +--- + +Feat: redact diff --git a/crates/loro-ffi/src/doc.rs b/crates/loro-ffi/src/doc.rs index c370bf40a..316cf99fb 100644 --- a/crates/loro-ffi/src/doc.rs +++ b/crates/loro-ffi/src/doc.rs @@ -345,10 +345,34 @@ impl LoroDoc { start_vv: &VersionVector, end_vv: &VersionVector, ) -> String { - let json = self - .doc - .export_json_updates_without_peer_compression(&start_vv.into(), &end_vv.into()); - serde_json::to_string(&json).unwrap() + serde_json::to_string( + &self + .doc + .export_json(&start_vv.into(), &end_vv.into(), false), + ) + .unwrap() + } + + /// Redacts sensitive content in JSON updates within the specified version range. + /// + /// This function allows you to share document history while removing potentially sensitive content. + /// It preserves the document structure and collaboration capabilities while replacing content with + /// placeholders according to these redaction rules: + /// + /// - Preserves delete and move operations + /// - Replaces text insertion content with the Unicode replacement character + /// - Substitutes list and map insert values with null + /// - Maintains structure of child containers + /// - Replaces text mark values with null + /// - Preserves map keys and text annotation keys + pub fn redact_json_updates( + &self, + json: &str, + version_range: &VersionRange, + ) -> Result { + let mut schema: JsonSchema = serde_json::from_str(json)?; + loro::encoding::json_schema::json::redact(&mut schema, version_range.clone())?; + Ok(serde_json::to_string(&schema).unwrap()) } /// Export the readable [`Change`]s in the given [`IdSpan`] diff --git a/crates/loro-internal/Cargo.toml b/crates/loro-internal/Cargo.toml index 3fd28deca..54bf1ed17 100644 --- a/crates/loro-internal/Cargo.toml +++ b/crates/loro-internal/Cargo.toml @@ -65,7 +65,7 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(loom)'] } version = "0.2.15" features = ["js"] -[target.'cfg(all(target_arch = "wasm32", not(feature = "wasm")))'.dependencies] +[target.'cfg(all(target_arch = "wasm32", not(features = "wasm")))'.dependencies] wasm-bindgen = "0.2.100" [dev-dependencies] diff --git a/crates/loro-internal/VERSION b/crates/loro-internal/VERSION index 3e1ad720b..5ebba4f08 100644 --- a/crates/loro-internal/VERSION +++ b/crates/loro-internal/VERSION @@ -1 +1 @@ -1.5.0 \ No newline at end of file +1.5.5 \ No newline at end of file diff --git a/crates/loro-wasm/deno.lock b/crates/loro-wasm/deno.lock index 0f0beaf46..cb7117e52 100644 --- a/crates/loro-wasm/deno.lock +++ b/crates/loro-wasm/deno.lock @@ -1,5 +1,5 @@ { - "version": "4", + "version": "5", "redirects": { "https://deno.land/std/archive/mod.ts": "https://deno.land/std@0.224.0/archive/mod.ts", "https://deno.land/std/encoding/base64.ts": "https://deno.land/std@0.224.0/encoding/base64.ts", @@ -216,6 +216,7 @@ "npm:@rollup/plugin-wasm@^6.2.2", "npm:@typescript-eslint/parser@^6.2.0", "npm:@vitest/ui@^1.0.4", + "npm:brotli-wasm@^3.0.1", "npm:esbuild@~0.18.20", "npm:eslint@^8.46.0", "npm:loro-crdt@0.16.0", diff --git a/crates/loro-wasm/package.json b/crates/loro-wasm/package.json index 64f7c636c..4eede1102 100644 --- a/crates/loro-wasm/package.json +++ b/crates/loro-wasm/package.json @@ -42,6 +42,7 @@ "@rollup/plugin-wasm": "^6.2.2", "@typescript-eslint/parser": "^6.2.0", "@vitest/ui": "^1.0.4", + "brotli-wasm": "^3.0.1", "esbuild": "^0.18.20", "eslint": "^8.46.0", "loro-crdt-alpha-4": "npm:loro-crdt@=1.0.0-alpha.4", diff --git a/crates/loro-wasm/scripts/build.ts b/crates/loro-wasm/scripts/build.ts index 6153709b6..a12dead29 100644 --- a/crates/loro-wasm/scripts/build.ts +++ b/crates/loro-wasm/scripts/build.ts @@ -1,5 +1,6 @@ import * as path from "https://deno.land/std@0.105.0/path/mod.ts"; import { gunzip, gzip } from "https://deno.land/x/compress@v0.4.5/mod.ts"; +import brotliPromise from "npm:brotli-wasm"; const __dirname = path.dirname(path.fromFileUrl(import.meta.url)); // deno run -A build.ts debug @@ -62,6 +63,11 @@ async function build() { console.log("Wasm size: ", (wasm.length / 1024).toFixed(2), "KB"); const gzipped = await gzip(wasm); console.log("Gzipped size: ", (gzipped.length / 1024).toFixed(2), "KB"); + + // Use brotli-wasm for brotli compression + const brotli = await brotliPromise; + const brotliCompressed = brotli.compress(wasm); + console.log("Brotli size: ", (brotliCompressed.length / 1024).toFixed(2), "KB"); } } diff --git a/crates/loro-wasm/src/convert.rs b/crates/loro-wasm/src/convert.rs index 283e10e32..5c7255661 100644 --- a/crates/loro-wasm/src/convert.rs +++ b/crates/loro-wasm/src/convert.rs @@ -5,14 +5,17 @@ use loro_internal::delta::{ResolvedMapDelta, ResolvedMapValue}; use loro_internal::encoding::{ImportBlobMetadata, ImportStatus}; use loro_internal::event::{Diff, ListDeltaMeta, ListDiff, TextDiff, TextMeta}; use loro_internal::handler::{Handler, ValueOrHandler}; +use loro_internal::json::JsonSchema; use loro_internal::version::VersionRange; use loro_internal::StringSlice; use loro_internal::{Counter, CounterSpan, FxHashMap, IdSpan, ListDiffItem}; +use serde::Serialize; use wasm_bindgen::{JsCast, JsValue}; use crate::{ - frontiers_to_ids, Container, Cursor, JsContainer, JsIdSpan, JsImportBlobMetadata, JsResult, - LoroCounter, LoroList, LoroMap, LoroMovableList, LoroText, LoroTree, VersionVector, + frontiers_to_ids, Container, Cursor, JsContainer, JsIdSpan, JsImportBlobMetadata, JsJsonSchema, + JsJsonSchemaOrString, JsResult, LoroCounter, LoroList, LoroMap, LoroMovableList, LoroText, + LoroTree, VersionVector, }; use wasm_bindgen::__rt::IntoJsResult; use wasm_bindgen::convert::RefFromWasmAbi; @@ -641,3 +644,29 @@ pub(crate) fn js_value_to_loro_value(js: &JsValue) -> LoroValue { LoroValue::Null } } + +/// Convert a JavaScript JsonSchema (or string) to Loro's internal JsonSchema +pub(crate) fn js_json_schema_to_loro_json_schema( + json: JsJsonSchemaOrString, +) -> JsResult { + let js_value: JsValue = json.into(); + + if js_value.is_string() { + let json_str = js_value.as_string().unwrap(); + JsonSchema::try_from(json_str.as_str()) + .map_err(|e| JsValue::from_str(&format!("Invalid JSON format: {}", e))) + } else { + serde_wasm_bindgen::from_value(js_value) + .map_err(|e| JsValue::from_str(&format!("Failed to parse JsonSchema: {}", e))) + } +} + +/// Convert Loro's internal JsonSchema to JavaScript JsonSchema +pub(crate) fn loro_json_schema_to_js_json_schema(json_schema: JsonSchema) -> JsJsonSchema { + let s = serde_wasm_bindgen::Serializer::new().serialize_maps_as_objects(true); + let value = json_schema + .serialize(&s) + .map_err(std::convert::Into::::into) + .unwrap(); + value.into() +} diff --git a/crates/loro-wasm/src/lib.rs b/crates/loro-wasm/src/lib.rs index ec8001426..b411315ad 100644 --- a/crates/loro-wasm/src/lib.rs +++ b/crates/loro-wasm/src/lib.rs @@ -5,8 +5,8 @@ // #![warn(missing_docs)] use convert::{ - import_status_to_js_value, js_diff_to_inner_diff, js_to_id_span, js_to_version_vector, - resolved_diff_to_js, + import_status_to_js_value, js_diff_to_inner_diff, js_json_schema_to_loro_json_schema, + js_to_id_span, js_to_version_vector, loro_json_schema_to_js_json_schema, resolved_diff_to_js, }; use js_sys::{Array, Object, Promise, Reflect, Uint8Array}; use loro_internal::{ @@ -25,7 +25,7 @@ use loro_internal::{ loro::{CommitOptions, ExportMode}, loro_common::{check_root_container_name, IdSpanVector}, undo::{DiffBatch, UndoItemMeta, UndoOrRedo}, - version::Frontiers, + version::{Frontiers, VersionRange}, ContainerType, DiffEvent, FxHashMap, HandlerTrait, IdSpan, LoroDoc as LoroDocInner, LoroResult, LoroValue, MovableListHandler, Subscription, TreeNodeWithChildren, TreeParentId, UndoManager as InnerUndoManager, VersionVector as InternalVersionVector, @@ -1434,11 +1434,8 @@ impl LoroDoc { json_end_vv, with_peer_compression.unwrap_or(true), ); - let s = serde_wasm_bindgen::Serializer::new().serialize_maps_as_objects(true); - let v = json_schema - .serialize(&s) - .map_err(std::convert::Into::::into)?; - Ok(v.into()) + + Ok(loro_json_schema_to_js_json_schema(json_schema)) } #[wasm_bindgen(js_name = "exportJsonInIdSpan", skip_typescript)] @@ -1457,16 +1454,7 @@ impl LoroDoc { /// only supports backward compatibility but not forward compatibility. #[wasm_bindgen(js_name = "importJsonUpdates")] pub fn import_json_updates(&self, json: JsJsonSchemaOrString) -> JsResult { - let json: JsValue = json.into(); - if JsValue::is_string(&json) { - let json_str = json.as_string().unwrap(); - let status = self - .0 - .import_json_updates(json_str.as_str()) - .map_err(JsValue::from)?; - return Ok(import_status_to_js_value(status).into()); - } - let json_schema: JsonSchema = serde_wasm_bindgen::from_value(json)?; + let json_schema = js_json_schema_to_loro_json_schema(json)?; let status = self.0.import_json_updates(json_schema)?; Ok(import_status_to_js_value(status).into()) } @@ -2088,11 +2076,7 @@ impl LoroDoc { /// ``` pub fn getUncommittedOpsAsJson(&self) -> JsResult> { let json_schema = self.0.get_uncommitted_ops_as_json(); - let s = serde_wasm_bindgen::Serializer::new().serialize_maps_as_objects(true); - let v = json_schema - .serialize(&s) - .map_err(std::convert::Into::::into)?; - Ok(Some(v.into())) + Ok(json_schema.and_then(|json| Some(loro_json_schema_to_js_json_schema(json)))) } #[wasm_bindgen(js_name = "subscribeFirstCommitFromPeer", skip_typescript)] @@ -5263,6 +5247,75 @@ impl ChangeModifier { } } +fn js_value_to_version_range(value: JsValue) -> JsResult { + let obj = js_sys::Object::from(value); + let entries = js_sys::Object::entries(&obj); + let mut range = VersionRange::new(); + + for i in 0..entries.length() { + let entry = entries.get(i); + let key_value = js_sys::Array::from(&entry); + + let peer_str = key_value + .get(0) + .as_string() + .ok_or_else(|| JsValue::from_str("Version range peer key must be a string"))?; + + let range_array = js_sys::Array::from(&key_value.get(1)); + if range_array.length() != 2 { + return Err(JsValue::from_str("Version range must be [start, end]")); + } + + let start = range_array + .get(0) + .as_f64() + .ok_or_else(|| JsValue::from_str("Range start must be a number"))? + as Counter; + + let end = range_array + .get(1) + .as_f64() + .ok_or_else(|| JsValue::from_str("Range end must be a number"))? + as Counter; + + let peer_id = js_peer_to_peer(JsValue::from_str(&peer_str))?; + range.insert(peer_id, start, end); + } + + Ok(range) +} + +/// Redacts sensitive content in JSON updates within the specified version range. +/// +/// This function allows you to share document history while removing potentially sensitive content. +/// It preserves the document structure and collaboration capabilities while replacing content with +/// placeholders according to these redaction rules: +/// +/// - Preserves delete and move operations +/// - Replaces text insertion content with the Unicode replacement character +/// - Substitutes list and map insert values with null +/// - Maintains structure of child containers +/// - Replaces text mark values with null +/// - Preserves map keys and text annotation keys +/// +/// @param {Object|string} jsonUpdates - The JSON updates to redact (object or JSON string) +/// @param {Object} versionRange - Version range defining what content to redact, +/// format: { peerId: [startCounter, endCounter], ... } +/// @returns {Object} The redacted JSON updates +#[wasm_bindgen(js_name = "redactJsonUpdates")] +pub fn redact_json_updates( + json_updates: JsJsonSchemaOrString, + version_range: JsValue, +) -> JsResult { + let mut loro_json = js_json_schema_to_loro_json_schema(json_updates)?; + let version_range = js_value_to_version_range(version_range)?; + + loro_internal::json::redact(&mut loro_json, version_range) + .map_err(|e| JsValue::from_str(&format!("Failed to redact JSON: {}", e)))?; + + Ok(loro_json_schema_to_js_json_schema(loro_json)) +} + #[wasm_bindgen(typescript_custom_section)] const TYPES: &'static str = r#" /** @@ -5827,10 +5880,6 @@ interface LoroMovableList { } export type Side = -1 | 0 | 1; -"#; - -#[wasm_bindgen(typescript_custom_section)] -const JSON_SCHEMA_TYPES: &'static str = r#" export type JsonOpID = `${number}@${PeerID}`; export type JsonContainerID = `🦜:${ContainerID}` ; export type JsonValue = @@ -6172,7 +6221,7 @@ interface LoroDoc = Record(name: Key): T[Key] extends LoroMovableList ? T[Key] : LoroMovableList; diff --git a/crates/loro-wasm/tests/basic.test.ts b/crates/loro-wasm/tests/basic.test.ts index 74472c672..57c47d196 100644 --- a/crates/loro-wasm/tests/basic.test.ts +++ b/crates/loro-wasm/tests/basic.test.ts @@ -19,6 +19,7 @@ import { ContainerID, LoroCounter, JsonDiff, + redactJsonUpdates, } from "../bundler/index"; it("basic example", () => { @@ -1527,6 +1528,56 @@ it("test container existence", () => { expect(doc2.hasContainer("cid:1@1:List")).toBe(true); }); +it("redactJsonUpdates removes sensitive content", () => { + const doc = new LoroDoc(); + doc.setPeerId("1"); + + // Create some content to be redacted + const text = doc.getText("text"); + text.insert(0, "Sensitive information"); + doc.commit(); + + const map = doc.getMap("map"); + map.set("password", "secret123"); + map.set("public", "public information"); + doc.commit(); + + // Export JSON updates + const jsonUpdates = doc.exportJsonUpdates(); + + // Define version range to redact (redact the text content) + const versionRange = { + "1": [0, 21] // Redact the "Sensitive information" + }; + + // Apply redaction + const redactedJson = redactJsonUpdates(jsonUpdates, versionRange); + + // Verify redacted content is replaced + const redactedDoc = new LoroDoc(); + redactedDoc.importJsonUpdates(redactedJson); + + // Text should be redacted with replacement character + expect(redactedDoc.getText("text").toString()).toBe("���������������������"); + + // Map operations after counter 5 should be intact + expect(redactedDoc.getMap("map").get("password")).toBe("secret123"); + expect(redactedDoc.getMap("map").get("public")).toBe("public information"); + + // Now redact the map content + const versionRange2 = { + "1": [21, 22] // Redact the "secret123" + }; + + const redactedJson2 = redactJsonUpdates(jsonUpdates, versionRange2); + const redactedDoc2 = new LoroDoc(); + redactedDoc2.importJsonUpdates(redactedJson2); + + expect(redactedDoc.getText("text").toString()).toBe("���������������������"); + expect(redactedDoc2.getMap("map").get("password")).toBe(null); + expect(redactedDoc.getMap("map").get("public")).toBe("public information"); +}); + it("text mark on LoroText", () => { const text = new LoroText(); text.insert(0, "Hello"); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 91c92f9ff..3f4c5ddb0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: '@vitest/ui': specifier: ^1.0.4 version: 1.6.0(vitest@1.6.0) + brotli-wasm: + specifier: ^3.0.1 + version: 3.0.1 esbuild: specifier: ^0.18.20 version: 0.18.20 @@ -91,17 +94,17 @@ importers: version: 1.3.7 vue: specifier: ^3.2.47 - version: 3.4.27(typescript@5.4.5) + version: 3.4.27(typescript@5.6.3) devDependencies: '@types/quill': specifier: ^1.3.7 version: 1.3.10 '@vitejs/plugin-vue': specifier: ^4.1.0 - version: 4.6.2(vite@4.5.3)(vue@3.4.27(typescript@5.4.5)) + version: 4.6.2(vite@4.5.3)(vue@3.4.27(typescript@5.6.3)) typescript: specifier: ^5.2.0 - version: 5.4.5 + version: 5.6.3 vite: specifier: ^4.3.2 version: 4.5.3 @@ -113,7 +116,7 @@ importers: version: 3.3.0(vite@4.5.3) vue-tsc: specifier: ^1.4.2 - version: 1.8.27(typescript@5.4.5) + version: 1.8.27(typescript@5.6.3) packages: @@ -961,6 +964,10 @@ packages: breakword@1.0.6: resolution: {integrity: sha512-yjxDAYyK/pBvws9H4xKYpLDpYKEH6CzrBPAuXq3x18I+c/2MkVtT3qAr7Oloi6Dss9qNhPVueAAVU1CSeNDIXw==} + brotli-wasm@3.0.1: + resolution: {integrity: sha512-U3K72/JAi3jITpdhZBqzSUq+DUY697tLxOuFXB+FpAE/Ug+5C3VZrv4uA674EUZHxNAuQ9wETXNqQkxZD6oL4A==} + engines: {node: '>=v18.0.0'} + builtin-modules@3.3.0: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} @@ -2327,11 +2334,6 @@ packages: resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} engines: {node: '>= 0.4'} - typescript@5.4.5: - resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} - engines: {node: '>=14.17'} - hasBin: true - typescript@5.6.3: resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} engines: {node: '>=14.17'} @@ -3150,10 +3152,10 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-vue@4.6.2(vite@4.5.3)(vue@3.4.27(typescript@5.4.5))': + '@vitejs/plugin-vue@4.6.2(vite@4.5.3)(vue@3.4.27(typescript@5.6.3))': dependencies: vite: 4.5.3 - vue: 3.4.27(typescript@5.4.5) + vue: 3.4.27(typescript@5.6.3) '@vitest/expect@1.6.0': dependencies: @@ -3238,7 +3240,7 @@ snapshots: '@vue/compiler-dom': 3.4.27 '@vue/shared': 3.4.27 - '@vue/language-core@1.8.27(typescript@5.4.5)': + '@vue/language-core@1.8.27(typescript@5.6.3)': dependencies: '@volar/language-core': 1.11.1 '@volar/source-map': 1.11.1 @@ -3250,7 +3252,7 @@ snapshots: path-browserify: 1.0.1 vue-template-compiler: 2.7.16 optionalDependencies: - typescript: 5.4.5 + typescript: 5.6.3 '@vue/reactivity@3.4.27': dependencies: @@ -3267,11 +3269,11 @@ snapshots: '@vue/shared': 3.4.27 csstype: 3.1.3 - '@vue/server-renderer@3.4.27(vue@3.4.27(typescript@5.4.5))': + '@vue/server-renderer@3.4.27(vue@3.4.27(typescript@5.6.3))': dependencies: '@vue/compiler-ssr': 3.4.27 '@vue/shared': 3.4.27 - vue: 3.4.27(typescript@5.4.5) + vue: 3.4.27(typescript@5.6.3) '@vue/shared@3.4.27': {} @@ -3366,6 +3368,8 @@ snapshots: dependencies: wcwidth: 1.0.1 + brotli-wasm@3.0.1: {} + builtin-modules@3.3.0: {} cac@6.7.14: {} @@ -4889,8 +4893,6 @@ snapshots: is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 - typescript@5.4.5: {} - typescript@5.6.3: {} ufo@1.5.3: {} @@ -5010,22 +5012,22 @@ snapshots: de-indent: 1.0.2 he: 1.2.0 - vue-tsc@1.8.27(typescript@5.4.5): + vue-tsc@1.8.27(typescript@5.6.3): dependencies: '@volar/typescript': 1.11.1 - '@vue/language-core': 1.8.27(typescript@5.4.5) + '@vue/language-core': 1.8.27(typescript@5.6.3) semver: 7.6.2 - typescript: 5.4.5 + typescript: 5.6.3 - vue@3.4.27(typescript@5.4.5): + vue@3.4.27(typescript@5.6.3): dependencies: '@vue/compiler-dom': 3.4.27 '@vue/compiler-sfc': 3.4.27 '@vue/runtime-dom': 3.4.27 - '@vue/server-renderer': 3.4.27(vue@3.4.27(typescript@5.4.5)) + '@vue/server-renderer': 3.4.27(vue@3.4.27(typescript@5.6.3)) '@vue/shared': 3.4.27 optionalDependencies: - typescript: 5.4.5 + typescript: 5.6.3 wcwidth@1.0.1: dependencies: From 0344223a11d0a9551b649ae57321de59c3d403de Mon Sep 17 00:00:00 2001 From: Zixuan Chen Date: Mon, 19 May 2025 11:52:11 +0800 Subject: [PATCH 2/8] chore: fix warnings --- crates/loro-ffi/src/doc.rs | 10 +++++--- .../src/container/richtext/tracker.rs | 1 - .../loro-internal/src/oplog/change_store.rs | 1 + .../loro-internal/src/state/counter_state.rs | 1 - crates/loro-wasm/src/lib.rs | 25 +++++++++---------- crates/loro/src/lib.rs | 2 +- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/crates/loro-ffi/src/doc.rs b/crates/loro-ffi/src/doc.rs index 316cf99fb..a7cb3b83a 100644 --- a/crates/loro-ffi/src/doc.rs +++ b/crates/loro-ffi/src/doc.rs @@ -348,7 +348,7 @@ impl LoroDoc { serde_json::to_string( &self .doc - .export_json(&start_vv.into(), &end_vv.into(), false), + .export_json_updates_without_peer_compression(&start_vv.into(), &end_vv.into()), ) .unwrap() } @@ -368,10 +368,12 @@ impl LoroDoc { pub fn redact_json_updates( &self, json: &str, - version_range: &VersionRange, + version_range: VersionRange, ) -> Result { - let mut schema: JsonSchema = serde_json::from_str(json)?; - loro::encoding::json_schema::json::redact(&mut schema, version_range.clone())?; + let mut schema: JsonSchema = + serde_json::from_str(json).map_err(|_e| LoroError::InvalidJsonSchema)?; + loro::json::redact(&mut schema, version_range) + .map_err(|e| LoroError::Unknown(e.to_string().into_boxed_str()))?; Ok(serde_json::to_string(&schema).unwrap()) } diff --git a/crates/loro-internal/src/container/richtext/tracker.rs b/crates/loro-internal/src/container/richtext/tracker.rs index 9b8896886..07761bc4c 100644 --- a/crates/loro-internal/src/container/richtext/tracker.rs +++ b/crates/loro-internal/src/container/richtext/tracker.rs @@ -220,7 +220,6 @@ impl Tracker { self.applied_vv.extend_to_include_end_id(end_id); } - #[must_use] fn skip_applied( &mut self, op_id: ID, diff --git a/crates/loro-internal/src/oplog/change_store.rs b/crates/loro-internal/src/oplog/change_store.rs index 2927f3d3f..305c91602 100644 --- a/crates/loro-internal/src/oplog/change_store.rs +++ b/crates/loro-internal/src/oplog/change_store.rs @@ -1240,6 +1240,7 @@ impl ChangesBlock { self.estimated_size > MAX_BLOCK_SIZE } + #[allow(clippy::result_large_err)] fn push_change( self: &mut Arc, change: Change, diff --git a/crates/loro-internal/src/state/counter_state.rs b/crates/loro-internal/src/state/counter_state.rs index b09e1d8d2..9b15c5ed2 100644 --- a/crates/loro-internal/src/state/counter_state.rs +++ b/crates/loro-internal/src/state/counter_state.rs @@ -38,7 +38,6 @@ impl ContainerState for CounterState { false } - #[must_use] fn apply_diff_and_convert(&mut self, diff: InternalDiff, _ctx: DiffApplyContext) -> Diff { if let InternalDiff::Counter(diff) = diff { self.value += diff; diff --git a/crates/loro-wasm/src/lib.rs b/crates/loro-wasm/src/lib.rs index b411315ad..4340b0782 100644 --- a/crates/loro-wasm/src/lib.rs +++ b/crates/loro-wasm/src/lib.rs @@ -21,7 +21,6 @@ use loro_internal::{ ValueOrHandler, }, id::{Counter, PeerID, TreeID, ID}, - json::JsonSchema, loro::{CommitOptions, ExportMode}, loro_common::{check_root_container_name, IdSpanVector}, undo::{DiffBatch, UndoItemMeta, UndoOrRedo}, @@ -2076,7 +2075,7 @@ impl LoroDoc { /// ``` pub fn getUncommittedOpsAsJson(&self) -> JsResult> { let json_schema = self.0.get_uncommitted_ops_as_json(); - Ok(json_schema.and_then(|json| Some(loro_json_schema_to_js_json_schema(json)))) + Ok(json_schema.map(loro_json_schema_to_js_json_schema)) } #[wasm_bindgen(js_name = "subscribeFirstCommitFromPeer", skip_typescript)] @@ -5487,11 +5486,11 @@ interface LoroDoc { * doc.commit(); * expect(doc.getChangeAt({ peer: "0", counter: 0 }).message).toBe("test"); * ``` - * + * * ### Advanced Example: Creating a Merkle DAG - * + * * By combining `doc.subscribePreCommit` with `doc.exportJsonInIdSpan`, you can implement advanced features like representing Loro's editing history as a Merkle DAG: - * + * * ```ts * const doc = new LoroDoc(); * doc.setPeerId(0); @@ -5511,7 +5510,7 @@ interface LoroDoc { * const sha256Hash = hash.digest('hex'); * e.modifier.setMessage(sha256Hash); * }); - * + * * doc.getList("list").insert(0, 100); * doc.commit(); * // Change 0 @@ -5529,8 +5528,8 @@ interface LoroDoc { * // } * // ] * // } - * - * + * + * * doc.getList("list").insert(0, 200); * doc.commit(); * // Change 1 @@ -5550,13 +5549,13 @@ interface LoroDoc { * // } * // ] * // } - * + * * expect(doc.getChangeAt({ peer: "0", counter: 0 }).message).toBe("2af99cf93869173984bcf6b1ce5412610b0413d027a5511a8f720a02a4432853"); * expect(doc.getChangeAt({ peer: "0", counter: 1 }).message).toBe("aedbb442c554ecf59090e0e8339df1d8febf647f25cc37c67be0c6e27071d37f"); * ``` - * + * * @param f - A callback function that receives a pre commit event. - * + * **/ subscribePreCommit(f: (e: { changeMeta: Change, origin: string, modifier: ChangeModifier }) => void): () => void @@ -6257,9 +6256,9 @@ interface LoroDoc = Record Date: Mon, 19 May 2025 14:32:31 +0800 Subject: [PATCH 3/8] ci: report wasm size in pr --- crates/loro-wasm/scripts/build.ts | 51 +++++++++++++++++++++++++++++-- package.json | 2 +- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/crates/loro-wasm/scripts/build.ts b/crates/loro-wasm/scripts/build.ts index a12dead29..b28975c3f 100644 --- a/crates/loro-wasm/scripts/build.ts +++ b/crates/loro-wasm/scripts/build.ts @@ -17,6 +17,11 @@ const TARGETS = ["bundler", "nodejs", "web"]; const startTime = performance.now(); const LoroWasmDir = path.resolve(__dirname, ".."); +// Check if running in CI +const isCI = Deno.env.get("CI") === "true"; +const githubToken = Deno.env.get("GITHUB_TOKEN"); +const githubEventPath = Deno.env.get("GITHUB_EVENT_PATH"); + console.log(LoroWasmDir); async function build() { await cargoBuild(); @@ -60,14 +65,54 @@ async function build() { if (profile === "release") { const wasm = await Deno.readFile(path.resolve(LoroWasmDir, "bundler", "loro_wasm_bg.wasm")); - console.log("Wasm size: ", (wasm.length / 1024).toFixed(2), "KB"); + const wasmSize = (wasm.length / 1024).toFixed(2); + console.log("Wasm size: ", wasmSize, "KB"); + const gzipped = await gzip(wasm); - console.log("Gzipped size: ", (gzipped.length / 1024).toFixed(2), "KB"); + const gzipSize = (gzipped.length / 1024).toFixed(2); + console.log("Gzipped size: ", gzipSize, "KB"); // Use brotli-wasm for brotli compression const brotli = await brotliPromise; const brotliCompressed = brotli.compress(wasm); - console.log("Brotli size: ", (brotliCompressed.length / 1024).toFixed(2), "KB"); + const brotliSize = (brotliCompressed.length / 1024).toFixed(2); + console.log("Brotli size: ", brotliSize, "KB"); + + // Report sizes to PR if in CI + if (isCI && githubToken && githubEventPath) { + try { + const event = JSON.parse(await Deno.readTextFile(githubEventPath)); + if (event.pull_request) { + const prNumber = event.pull_request.number; + const repo = event.repository.full_name; + + const comment = `## WASM Size Report +- Original size: ${wasmSize} KB +- Gzipped size: ${gzipSize} KB +- Brotli size: ${brotliSize} KB`; + + // Create or update comment + const response = await fetch( + `https://api.github.com/repos/${repo}/issues/${prNumber}/comments`, + { + method: "POST", + headers: { + "Authorization": `Bearer ${githubToken}`, + "Accept": "application/vnd.github.v3+json", + "Content-Type": "application/json", + }, + body: JSON.stringify({ body: comment }), + } + ); + + if (!response.ok) { + console.error("Failed to create PR comment:", await response.text()); + } + } + } catch (error) { + console.error("Failed to report sizes to PR:", error); + } + } } } diff --git a/package.json b/package.json index f6525f262..2b46f939f 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "check-all": "cargo hack check --each-feature", "build": "cargo build", "test": "cargo nextest run --features=test_utils,jsonpath --no-fail-fast && cargo test --doc", - "test-all": "pnpm test && pnpm test-wasm && pnpm test-loom", + "test-all": "pnpm test && pnpm release-wasm && pnpm test-loom", "test-wasm": "cd crates/loro-wasm && pnpm i && pnpm build-dev", "test-loom": "LOOM_MAX_PREEMPTIONS=2 RUSTFLAGS='--cfg loom' cargo test -p loro --test multi_thread_test --release", "test-loom-reproduce": "LOOM_MAX_PREEMPTIONS=2 LOOM_LOG=info LOOM_LOCATION=1 LOOM_CHECKPOINT_INTERVAL=1 LOOM_CHECKPOINT_FILE=loom_test.json RUSTFLAGS='--cfg loom' cargo test -p loro --test multi_thread_test --release", From 937e8cbf7212f31dd111dcaf37d0c95d763f7f87 Mon Sep 17 00:00:00 2001 From: Zixuan Chen Date: Mon, 19 May 2025 14:51:53 +0800 Subject: [PATCH 4/8] ci: pr comment --- crates/loro-wasm/package.json | 1 + crates/loro-wasm/scripts/build.ts | 47 ++++++--- pnpm-lock.yaml | 165 ++++++++++++++++++++++++++++++ 3 files changed, 198 insertions(+), 15 deletions(-) diff --git a/crates/loro-wasm/package.json b/crates/loro-wasm/package.json index 4eede1102..e2118b283 100644 --- a/crates/loro-wasm/package.json +++ b/crates/loro-wasm/package.json @@ -36,6 +36,7 @@ "author": "", "license": "MIT", "devDependencies": { + "@actions/github": "^6.0.1", "@rollup/plugin-alias": "^5.1.1", "@rollup/plugin-node-resolve": "^15.0.1", "@rollup/plugin-typescript": "^12.1.1", diff --git a/crates/loro-wasm/scripts/build.ts b/crates/loro-wasm/scripts/build.ts index b28975c3f..9d3e7dcde 100644 --- a/crates/loro-wasm/scripts/build.ts +++ b/crates/loro-wasm/scripts/build.ts @@ -1,6 +1,7 @@ import * as path from "https://deno.land/std@0.105.0/path/mod.ts"; import { gunzip, gzip } from "https://deno.land/x/compress@v0.4.5/mod.ts"; import brotliPromise from "npm:brotli-wasm"; +import { comment, getOctokit } from "npm:@actions/github"; const __dirname = path.dirname(path.fromFileUrl(import.meta.url)); // deno run -A build.ts debug @@ -85,28 +86,44 @@ async function build() { if (event.pull_request) { const prNumber = event.pull_request.number; const repo = event.repository.full_name; + const [owner, repoName] = repo.split("/"); - const comment = `## WASM Size Report + const commentBody = `## WASM Size Report + - Original size: ${wasmSize} KB - Gzipped size: ${gzipSize} KB - Brotli size: ${brotliSize} KB`; - // Create or update comment - const response = await fetch( - `https://api.github.com/repos/${repo}/issues/${prNumber}/comments`, - { - method: "POST", - headers: { - "Authorization": `Bearer ${githubToken}`, - "Accept": "application/vnd.github.v3+json", - "Content-Type": "application/json", - }, - body: JSON.stringify({ body: comment }), - } + // Get existing comments + const octokit = getOctokit(githubToken); + const { data: comments } = await octokit.rest.issues.listComments({ + owner, + repo: repoName, + issue_number: prNumber, + }); + + // Find our comment + const existingComment = comments.find(comment => + comment.body?.includes("") ); - if (!response.ok) { - console.error("Failed to create PR comment:", await response.text()); + if (existingComment) { + // Update existing comment + await octokit.rest.issues.updateComment({ + owner, + repo: repoName, + comment_id: existingComment.id, + body: commentBody, + }); + } else { + // Create new comment + await comment({ + token: githubToken, + owner, + repo: repoName, + issue_number: prNumber, + body: commentBody, + }); } } } catch (error) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3f4c5ddb0..91e94e2bf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: crates/loro-wasm: devDependencies: + '@actions/github': + specifier: ^6.0.1 + version: 6.0.1 '@rollup/plugin-alias': specifier: ^5.1.1 version: 5.1.1(rollup@3.29.4) @@ -120,6 +123,12 @@ importers: packages: + '@actions/github@6.0.1': + resolution: {integrity: sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw==} + + '@actions/http-client@2.2.3': + resolution: {integrity: sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==} + '@babel/code-frame@7.24.2': resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==} engines: {node: '>=6.9.0'} @@ -489,6 +498,10 @@ packages: resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@fastify/busboy@2.1.1': + resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} + engines: {node: '>=14'} + '@humanwhocodes/config-array@0.11.14': resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} @@ -527,6 +540,54 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@octokit/auth-token@4.0.0': + resolution: {integrity: sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==} + engines: {node: '>= 18'} + + '@octokit/core@5.2.1': + resolution: {integrity: sha512-dKYCMuPO1bmrpuogcjQ8z7ICCH3FP6WmxpwC03yjzGfZhj9fTJg6+bS1+UAplekbN2C+M61UNllGOOoAfGCrdQ==} + engines: {node: '>= 18'} + + '@octokit/endpoint@9.0.6': + resolution: {integrity: sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==} + engines: {node: '>= 18'} + + '@octokit/graphql@7.1.1': + resolution: {integrity: sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==} + engines: {node: '>= 18'} + + '@octokit/openapi-types@20.0.0': + resolution: {integrity: sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==} + + '@octokit/openapi-types@24.2.0': + resolution: {integrity: sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==} + + '@octokit/plugin-paginate-rest@9.2.2': + resolution: {integrity: sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '5' + + '@octokit/plugin-rest-endpoint-methods@10.4.1': + resolution: {integrity: sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '5' + + '@octokit/request-error@5.1.1': + resolution: {integrity: sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==} + engines: {node: '>= 18'} + + '@octokit/request@8.4.1': + resolution: {integrity: sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==} + engines: {node: '>= 18'} + + '@octokit/types@12.6.0': + resolution: {integrity: sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==} + + '@octokit/types@13.10.0': + resolution: {integrity: sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==} + '@polka/url@1.0.0-next.25': resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==} @@ -947,6 +1008,9 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + before-after-hook@2.2.3: + resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} + better-path-resolve@1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} @@ -1132,6 +1196,9 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + deprecation@2.3.1: + resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} + detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} @@ -2294,6 +2361,10 @@ packages: engines: {node: '>=8.0.0'} hasBin: true + tunnel@0.0.6: + resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} + engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -2345,6 +2416,13 @@ packages: unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + undici@5.29.0: + resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==} + engines: {node: '>=14.0'} + + universal-user-agent@6.0.1: + resolution: {integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==} + universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} @@ -2562,6 +2640,21 @@ packages: snapshots: + '@actions/github@6.0.1': + dependencies: + '@actions/http-client': 2.2.3 + '@octokit/core': 5.2.1 + '@octokit/plugin-paginate-rest': 9.2.2(@octokit/core@5.2.1) + '@octokit/plugin-rest-endpoint-methods': 10.4.1(@octokit/core@5.2.1) + '@octokit/request': 8.4.1 + '@octokit/request-error': 5.1.1 + undici: 5.29.0 + + '@actions/http-client@2.2.3': + dependencies: + tunnel: 0.0.6 + undici: 5.29.0 + '@babel/code-frame@7.24.2': dependencies: '@babel/highlight': 7.24.5 @@ -2898,6 +2991,8 @@ snapshots: '@eslint/js@8.57.0': {} + '@fastify/busboy@2.1.1': {} + '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.3 @@ -2944,6 +3039,64 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 + '@octokit/auth-token@4.0.0': {} + + '@octokit/core@5.2.1': + dependencies: + '@octokit/auth-token': 4.0.0 + '@octokit/graphql': 7.1.1 + '@octokit/request': 8.4.1 + '@octokit/request-error': 5.1.1 + '@octokit/types': 13.10.0 + before-after-hook: 2.2.3 + universal-user-agent: 6.0.1 + + '@octokit/endpoint@9.0.6': + dependencies: + '@octokit/types': 13.10.0 + universal-user-agent: 6.0.1 + + '@octokit/graphql@7.1.1': + dependencies: + '@octokit/request': 8.4.1 + '@octokit/types': 13.10.0 + universal-user-agent: 6.0.1 + + '@octokit/openapi-types@20.0.0': {} + + '@octokit/openapi-types@24.2.0': {} + + '@octokit/plugin-paginate-rest@9.2.2(@octokit/core@5.2.1)': + dependencies: + '@octokit/core': 5.2.1 + '@octokit/types': 12.6.0 + + '@octokit/plugin-rest-endpoint-methods@10.4.1(@octokit/core@5.2.1)': + dependencies: + '@octokit/core': 5.2.1 + '@octokit/types': 12.6.0 + + '@octokit/request-error@5.1.1': + dependencies: + '@octokit/types': 13.10.0 + deprecation: 2.3.1 + once: 1.4.0 + + '@octokit/request@8.4.1': + dependencies: + '@octokit/endpoint': 9.0.6 + '@octokit/request-error': 5.1.1 + '@octokit/types': 13.10.0 + universal-user-agent: 6.0.1 + + '@octokit/types@12.6.0': + dependencies: + '@octokit/openapi-types': 20.0.0 + + '@octokit/types@13.10.0': + dependencies: + '@octokit/openapi-types': 24.2.0 + '@polka/url@1.0.0-next.25': {} '@rollup/plugin-alias@5.1.1(rollup@3.29.4)': @@ -3347,6 +3500,8 @@ snapshots: balanced-match@1.0.2: {} + before-after-hook@2.2.3: {} + better-path-resolve@1.0.0: dependencies: is-windows: 1.0.2 @@ -3546,6 +3701,8 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + deprecation@2.3.1: {} + detect-indent@6.1.0: {} diff-sequences@29.6.3: {} @@ -4847,6 +5004,8 @@ snapshots: wcwidth: 1.0.1 yargs: 17.7.2 + tunnel@0.0.6: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -4904,6 +5063,12 @@ snapshots: has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 + undici@5.29.0: + dependencies: + '@fastify/busboy': 2.1.1 + + universal-user-agent@6.0.1: {} + universalify@0.1.2: {} uri-js@4.4.1: From 5d2b3b71e84684a082e2f4f846ac5beddad2ee4a Mon Sep 17 00:00:00 2001 From: Zixuan Chen Date: Mon, 19 May 2025 15:04:29 +0800 Subject: [PATCH 5/8] ci: pr comment --- crates/loro-wasm/scripts/build.ts | 30 ++-- crates/loro-wasm/scripts/deno.json | 3 + crates/loro-wasm/scripts/deno.lock | 252 +++++++++++++++++++++++++++++ 3 files changed, 274 insertions(+), 11 deletions(-) create mode 100644 crates/loro-wasm/scripts/deno.json create mode 100644 crates/loro-wasm/scripts/deno.lock diff --git a/crates/loro-wasm/scripts/build.ts b/crates/loro-wasm/scripts/build.ts index 9d3e7dcde..63a44d30c 100644 --- a/crates/loro-wasm/scripts/build.ts +++ b/crates/loro-wasm/scripts/build.ts @@ -1,7 +1,7 @@ import * as path from "https://deno.land/std@0.105.0/path/mod.ts"; -import { gunzip, gzip } from "https://deno.land/x/compress@v0.4.5/mod.ts"; +import { gzip } from "https://deno.land/x/compress@v0.4.5/mod.ts"; import brotliPromise from "npm:brotli-wasm"; -import { comment, getOctokit } from "npm:@actions/github"; +import { getOctokit } from "npm:@actions/github"; const __dirname = path.dirname(path.fromFileUrl(import.meta.url)); // deno run -A build.ts debug @@ -65,7 +65,9 @@ async function build() { ); if (profile === "release") { - const wasm = await Deno.readFile(path.resolve(LoroWasmDir, "bundler", "loro_wasm_bg.wasm")); + const wasm = await Deno.readFile( + path.resolve(LoroWasmDir, "bundler", "loro_wasm_bg.wasm"), + ); const wasmSize = (wasm.length / 1024).toFixed(2); console.log("Wasm size: ", wasmSize, "KB"); @@ -82,6 +84,7 @@ async function build() { // Report sizes to PR if in CI if (isCI && githubToken && githubEventPath) { try { + // Parse GitHub event data const event = JSON.parse(await Deno.readTextFile(githubEventPath)); if (event.pull_request) { const prNumber = event.pull_request.number; @@ -94,17 +97,19 @@ async function build() { - Gzipped size: ${gzipSize} KB - Brotli size: ${brotliSize} KB`; - // Get existing comments + // Initialize Octokit client const octokit = getOctokit(githubToken); + + // Find if we already have a comment with our marker const { data: comments } = await octokit.rest.issues.listComments({ owner, repo: repoName, issue_number: prNumber, }); - // Find our comment - const existingComment = comments.find(comment => - comment.body?.includes("") + const sizeReportMarker = ""; + const existingComment = comments.find((comment) => + comment.body?.includes(sizeReportMarker) ); if (existingComment) { @@ -115,15 +120,16 @@ async function build() { comment_id: existingComment.id, body: commentBody, }); + console.log("Updated existing WASM size report comment"); } else { // Create new comment - await comment({ - token: githubToken, + await octokit.rest.issues.createComment({ owner, repo: repoName, issue_number: prNumber, body: commentBody, }); + console.log("Created new WASM size report comment"); } } } catch (error) { @@ -134,7 +140,8 @@ async function build() { } async function cargoBuild() { - const cmd = `cargo build --target wasm32-unknown-unknown --profile ${profile}`; + const cmd = + `cargo build --target wasm32-unknown-unknown --profile ${profile}`; console.log(cmd); const status = await Deno.run({ cmd: cmd.split(" "), @@ -162,7 +169,8 @@ async function buildTarget(target: string) { } // TODO: polyfill FinalizationRegistry - const cmd = `wasm-bindgen --weak-refs --target ${target} --out-dir ${target} ../../target/wasm32-unknown-unknown/${profileDir}/loro_wasm.wasm`; + const cmd = + `wasm-bindgen --weak-refs --target ${target} --out-dir ${target} ../../target/wasm32-unknown-unknown/${profileDir}/loro_wasm.wasm`; console.log(">", cmd); await Deno.run({ cmd: cmd.split(" "), cwd: LoroWasmDir }).status(); console.log(); diff --git a/crates/loro-wasm/scripts/deno.json b/crates/loro-wasm/scripts/deno.json new file mode 100644 index 000000000..f5b67263f --- /dev/null +++ b/crates/loro-wasm/scripts/deno.json @@ -0,0 +1,3 @@ +{ + "nodeModules": "auto" +} diff --git a/crates/loro-wasm/scripts/deno.lock b/crates/loro-wasm/scripts/deno.lock new file mode 100644 index 000000000..e4197300b --- /dev/null +++ b/crates/loro-wasm/scripts/deno.lock @@ -0,0 +1,252 @@ +{ + "version": "5", + "specifiers": { + "npm:@actions/github@*": "6.0.1_@octokit+core@5.2.1", + "npm:brotli-wasm@*": "3.0.1" + }, + "npm": { + "@actions/github@6.0.1_@octokit+core@5.2.1": { + "integrity": "sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw==", + "dependencies": [ + "@actions/http-client", + "@octokit/core", + "@octokit/plugin-paginate-rest", + "@octokit/plugin-rest-endpoint-methods", + "@octokit/request", + "@octokit/request-error", + "undici" + ] + }, + "@actions/http-client@2.2.3": { + "integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==", + "dependencies": [ + "tunnel", + "undici" + ] + }, + "@fastify/busboy@2.1.1": { + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==" + }, + "@octokit/auth-token@4.0.0": { + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==" + }, + "@octokit/core@5.2.1": { + "integrity": "sha512-dKYCMuPO1bmrpuogcjQ8z7ICCH3FP6WmxpwC03yjzGfZhj9fTJg6+bS1+UAplekbN2C+M61UNllGOOoAfGCrdQ==", + "dependencies": [ + "@octokit/auth-token", + "@octokit/graphql", + "@octokit/request", + "@octokit/request-error", + "@octokit/types@13.10.0", + "before-after-hook", + "universal-user-agent" + ] + }, + "@octokit/endpoint@9.0.6": { + "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", + "dependencies": [ + "@octokit/types@13.10.0", + "universal-user-agent" + ] + }, + "@octokit/graphql@7.1.1": { + "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==", + "dependencies": [ + "@octokit/request", + "@octokit/types@13.10.0", + "universal-user-agent" + ] + }, + "@octokit/openapi-types@20.0.0": { + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==" + }, + "@octokit/openapi-types@24.2.0": { + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==" + }, + "@octokit/plugin-paginate-rest@9.2.2_@octokit+core@5.2.1": { + "integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==", + "dependencies": [ + "@octokit/core", + "@octokit/types@12.6.0" + ] + }, + "@octokit/plugin-rest-endpoint-methods@10.4.1_@octokit+core@5.2.1": { + "integrity": "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==", + "dependencies": [ + "@octokit/core", + "@octokit/types@12.6.0" + ] + }, + "@octokit/request-error@5.1.1": { + "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", + "dependencies": [ + "@octokit/types@13.10.0", + "deprecation", + "once" + ] + }, + "@octokit/request@8.4.1": { + "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==", + "dependencies": [ + "@octokit/endpoint", + "@octokit/request-error", + "@octokit/types@13.10.0", + "universal-user-agent" + ] + }, + "@octokit/types@12.6.0": { + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", + "dependencies": [ + "@octokit/openapi-types@20.0.0" + ] + }, + "@octokit/types@13.10.0": { + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", + "dependencies": [ + "@octokit/openapi-types@24.2.0" + ] + }, + "before-after-hook@2.2.3": { + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" + }, + "brotli-wasm@3.0.1": { + "integrity": "sha512-U3K72/JAi3jITpdhZBqzSUq+DUY697tLxOuFXB+FpAE/Ug+5C3VZrv4uA674EUZHxNAuQ9wETXNqQkxZD6oL4A==" + }, + "deprecation@2.3.1": { + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "once@1.4.0": { + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": [ + "wrappy" + ] + }, + "tunnel@0.0.6": { + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, + "undici@5.29.0": { + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", + "dependencies": [ + "@fastify/busboy" + ] + }, + "universal-user-agent@6.0.1": { + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==" + }, + "wrappy@1.0.2": { + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + } + }, + "remote": { + "https://deno.land/std@0.105.0/_util/assert.ts": "2f868145a042a11d5ad0a3c748dcf580add8a0dbc0e876eaa0026303a5488f58", + "https://deno.land/std@0.105.0/_util/os.ts": "dfb186cc4e968c770ab6cc3288bd65f4871be03b93beecae57d657232ecffcac", + "https://deno.land/std@0.105.0/path/_constants.ts": "1247fee4a79b70c89f23499691ef169b41b6ccf01887a0abd131009c5581b853", + "https://deno.land/std@0.105.0/path/_interface.ts": "1fa73b02aaa24867e481a48492b44f2598cd9dfa513c7b34001437007d3642e4", + "https://deno.land/std@0.105.0/path/_util.ts": "2e06a3b9e79beaf62687196bd4b60a4c391d862cfa007a20fc3a39f778ba073b", + "https://deno.land/std@0.105.0/path/common.ts": "eaf03d08b569e8a87e674e4e265e099f237472b6fd135b3cbeae5827035ea14a", + "https://deno.land/std@0.105.0/path/glob.ts": "3b84af55c53febacf6afe214c095624b22a56b6f57d7312157479cc783a0de65", + "https://deno.land/std@0.105.0/path/mod.ts": "4465dc494f271b02569edbb4a18d727063b5dbd6ed84283ff906260970a15d12", + "https://deno.land/std@0.105.0/path/posix.ts": "b81974c768d298f8dcd2c720229639b3803ca4a241fa9a355c762fa2bc5ef0c1", + "https://deno.land/std@0.105.0/path/separator.ts": "8fdcf289b1b76fd726a508f57d3370ca029ae6976fcde5044007f062e643ff1c", + "https://deno.land/std@0.105.0/path/win32.ts": "f4a3d4a3f2c9fe894da046d5eac48b5e789a0ebec5152b2c0985efe96a9f7ae1", + "https://deno.land/std@0.129.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", + "https://deno.land/std@0.129.0/_util/os.ts": "49b92edea1e82ba295ec946de8ffd956ed123e2948d9bd1d3e901b04e4307617", + "https://deno.land/std@0.129.0/archive/tar.ts": "35ea1baddec7988cc4034765a2cee7613bc8074bd40940d3f5e98f63070a716a", + "https://deno.land/std@0.129.0/async/abortable.ts": "a896ac6b0d4237bd2d2d248217cfa1f0d85ccda93cb25ebda55e33850e526be6", + "https://deno.land/std@0.129.0/async/deadline.ts": "48ac998d7564969f3e6ec6b6f9bf0217ebd00239b1b2292feba61272d5dd58d0", + "https://deno.land/std@0.129.0/async/debounce.ts": "564273ef242bcfcda19a439132f940db8694173abffc159ea34f07d18fc42620", + "https://deno.land/std@0.129.0/async/deferred.ts": "bc18e28108252c9f67dfca2bbc4587c3cbf3aeb6e155f8c864ca8ecff992b98a", + "https://deno.land/std@0.129.0/async/delay.ts": "cbbdf1c87d1aed8edc7bae13592fb3e27e3106e0748f089c263390d4f49e5f6c", + "https://deno.land/std@0.129.0/async/mod.ts": "2240c6841157738414331f47dee09bb8c0482c5b1980b6e3234dd03515c8132f", + "https://deno.land/std@0.129.0/async/mux_async_iterator.ts": "f4d1d259b0c694d381770ddaaa4b799a94843eba80c17f4a2ec2949168e52d1e", + "https://deno.land/std@0.129.0/async/pool.ts": "97b0dd27c69544e374df857a40902e74e39532f226005543eabacb551e277082", + "https://deno.land/std@0.129.0/async/tee.ts": "1341feb1f5b1a96f8628d0f8fc07d8c43d3813423f18a63bf1b4785568d21b1f", + "https://deno.land/std@0.129.0/bytes/bytes_list.ts": "67eb118e0b7891d2f389dad4add35856f4ad5faab46318ff99653456c23b025d", + "https://deno.land/std@0.129.0/bytes/equals.ts": "fc16dff2090cced02497f16483de123dfa91e591029f985029193dfaa9d894c9", + "https://deno.land/std@0.129.0/bytes/mod.ts": "d3b455c0dbd4804644159d1e25946ade5ee385d2359894de49e2c6101b18b7a9", + "https://deno.land/std@0.129.0/encoding/base64.ts": "c8c16b4adaa60d7a8eee047c73ece26844435e8f7f1328d74593dbb2dd58ea4f", + "https://deno.land/std@0.129.0/encoding/base64url.ts": "55f9d13df02efac10c6f96169daa3e702606a64e8aa27c0295f645f198c27130", + "https://deno.land/std@0.129.0/fmt/colors.ts": "30455035d6d728394781c10755351742dd731e3db6771b1843f9b9e490104d37", + "https://deno.land/std@0.129.0/fmt/printf.ts": "e2c0f72146aed1efecf0c39ab928b26ae493a2278f670a871a0fbdcf36ff3379", + "https://deno.land/std@0.129.0/fs/_util.ts": "0fb24eb4bfebc2c194fb1afdb42b9c3dda12e368f43e8f2321f84fc77d42cb0f", + "https://deno.land/std@0.129.0/fs/ensure_dir.ts": "9dc109c27df4098b9fc12d949612ae5c9c7169507660dcf9ad90631833209d9d", + "https://deno.land/std@0.129.0/fs/ensure_file.ts": "7d353e64fee3d4d1e7c6b6726a2a5e987ba402c15fb49566309042887349c545", + "https://deno.land/std@0.129.0/io/buffer.ts": "bd0c4bf53db4b4be916ca5963e454bddfd3fcd45039041ea161dbf826817822b", + "https://deno.land/std@0.129.0/io/files.ts": "d199ef64e918a256320ba8d8d44ae91de87c9077df8f8d6cca013f1b9fbbe285", + "https://deno.land/std@0.129.0/io/readers.ts": "679471f3b9929b54393c9cd75b6bd178b4bc6d9aab5c0f1f9538f862cf4746fe", + "https://deno.land/std@0.129.0/io/util.ts": "078da53bba767bec0d45f7da44411f6dbf269e51ef7fcfea5e3714e04681c674", + "https://deno.land/std@0.129.0/node/_buffer.mjs": "f4a7df481d4eed06dc0151b833177d8ef74fc3a96dd4d2b073e690b6ced9474d", + "https://deno.land/std@0.129.0/node/_core.ts": "568d277be2e086af996cbdd599fec569f5280e9a494335ca23ad392b130d7bb9", + "https://deno.land/std@0.129.0/node/_events.mjs": "c0e3e0e290a8b81fee9d2973a529c8dcd5ebb4406782d1f91085274e2cb8490f", + "https://deno.land/std@0.129.0/node/_fixed_queue.ts": "455b3c484de48e810b13bdf95cd1658ecb1ba6bcb8b9315ffe994efcde3ba5f5", + "https://deno.land/std@0.129.0/node/_next_tick.ts": "64c361f6bca21df2a72dd77b84bd49d80d97a694dd3080703bc78f52146351d1", + "https://deno.land/std@0.129.0/node/_process/exiting.ts": "bc9694769139ffc596f962087155a8bfef10101d03423b9dcbc51ce6e1f88fce", + "https://deno.land/std@0.129.0/node/_util/_util_callbackify.ts": "79928ad80df3e469f7dcdb198118a7436d18a9f6c08bd7a4382332ad25a718cf", + "https://deno.land/std@0.129.0/node/_utils.ts": "c2c352e83c4c96f5ff994b1c8246bff2abcb21bfc3f1c06162cb3af1d201e615", + "https://deno.land/std@0.129.0/node/buffer.ts": "fbecbf3f237fa49bec96e97ecf56a7b92d48037b3d11219288e68943cc921600", + "https://deno.land/std@0.129.0/node/events.ts": "a1d40fc0dbccc944379ef968b80ea08f9fce579e88b5057fdb64e4f0812476dd", + "https://deno.land/std@0.129.0/node/internal/buffer.mjs": "6662fe7fe517329453545be34cea27a24f8ccd6d09afd4f609f11ade2b6dfca7", + "https://deno.land/std@0.129.0/node/internal/crypto/keys.ts": "16ce7b15a9fc5e4e3dee8fde75dae12f3d722558d5a1a6e65a9b4f86d64a21e9", + "https://deno.land/std@0.129.0/node/internal/crypto/util.mjs": "1de55a47fdbed6721b467a77ba48fdd1550c10b5eee77bbdb602eaffee365a5e", + "https://deno.land/std@0.129.0/node/internal/error_codes.ts": "ac03c4eae33de3a69d6c98e8678003207eecf75a6900eb847e3fea3c8c9e6d8f", + "https://deno.land/std@0.129.0/node/internal/errors.ts": "0d3a1eb03b654beb29b8354759a6902f45a840d4f957e9a3c632a24ce4c32632", + "https://deno.land/std@0.129.0/node/internal/hide_stack_frames.ts": "a91962ec84610bc7ec86022c4593cdf688156a5910c07b5bcd71994225c13a03", + "https://deno.land/std@0.129.0/node/internal/normalize_encoding.mjs": "3779ec8a7adf5d963b0224f9b85d1bc974a2ec2db0e858396b5d3c2c92138a0a", + "https://deno.land/std@0.129.0/node/internal/util.mjs": "684653b962fae84fd2bc08997291b1a50bed09b95dcfa7d35e3c4143163e879a", + "https://deno.land/std@0.129.0/node/internal/util/comparisons.ts": "680b55fe8bdf1613633bc469fa0440f43162c76dbe36af9aa2966310e1bb9f6e", + "https://deno.land/std@0.129.0/node/internal/util/debuglog.ts": "99e91bdf26f6c67861031f684817e1705a5bc300e81346585b396f413387edfb", + "https://deno.land/std@0.129.0/node/internal/util/inspect.mjs": "d1c2569c66a3dab45eec03208f22ad4351482527859c0011a28a6c797288a0aa", + "https://deno.land/std@0.129.0/node/internal/util/types.ts": "b2dacb8f1f5d28a51c4da5c5b75172b7fcf694073ce95ca141323657e18b0c60", + "https://deno.land/std@0.129.0/node/internal/validators.mjs": "a7e82eafb7deb85c332d5f8d9ffef052f46a42d4a121eada4a54232451acc49a", + "https://deno.land/std@0.129.0/node/internal_binding/_libuv_winerror.ts": "801e05c2742ae6cd42a5f0fd555a255a7308a65732551e962e5345f55eedc519", + "https://deno.land/std@0.129.0/node/internal_binding/_node.ts": "e4075ba8a37aef4eb5b592c8e3807c39cb49ca8653faf8e01a43421938076c1b", + "https://deno.land/std@0.129.0/node/internal_binding/_utils.ts": "1c50883b5751a9ea1b38951e62ed63bacfdc9d69ea665292edfa28e1b1c5bd94", + "https://deno.land/std@0.129.0/node/internal_binding/_winerror.ts": "8811d4be66f918c165370b619259c1f35e8c3e458b8539db64c704fbde0a7cd2", + "https://deno.land/std@0.129.0/node/internal_binding/buffer.ts": "722c62b85f966e0777b2d98c021b60e75d7f2c2dabc43413ef37d60dbd13a5d9", + "https://deno.land/std@0.129.0/node/internal_binding/constants.ts": "aff06aac49eda4234bd3a2b0b8e1fbfc67824e281c532ff9960831ab503014cc", + "https://deno.land/std@0.129.0/node/internal_binding/string_decoder.ts": "5cb1863763d1e9b458bc21d6f976f16d9c18b3b3f57eaf0ade120aee38fba227", + "https://deno.land/std@0.129.0/node/internal_binding/types.ts": "4c26fb74ba2e45de553c15014c916df6789529a93171e450d5afb016b4c765e7", + "https://deno.land/std@0.129.0/node/internal_binding/util.ts": "90364292e2bd598ab5d105b48ca49817b6708f2d1d9cbaf08b2b3ab5ca4c90a7", + "https://deno.land/std@0.129.0/node/internal_binding/uv.ts": "3821bc5e676d6955d68f581988c961d77dd28190aba5a9c59f16001a4deb34ba", + "https://deno.land/std@0.129.0/node/util.ts": "7fd6933b37af89a8e64d73dc6ee1732455a59e7e6d0965311fbd73cd634ea630", + "https://deno.land/std@0.129.0/node/util/types.mjs": "f9288198cacd374b41bae7e92a23179d3160f4c0eaf14e19be3a4e7057219a60", + "https://deno.land/std@0.129.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", + "https://deno.land/std@0.129.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", + "https://deno.land/std@0.129.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b", + "https://deno.land/std@0.129.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", + "https://deno.land/std@0.129.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee", + "https://deno.land/std@0.129.0/path/mod.ts": "4275129bb766f0e475ecc5246aa35689eeade419d72a48355203f31802640be7", + "https://deno.land/std@0.129.0/path/posix.ts": "663e4a6fe30a145f56aa41a22d95114c4c5582d8b57d2d7c9ed27ad2c47636bb", + "https://deno.land/std@0.129.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", + "https://deno.land/std@0.129.0/path/win32.ts": "e7bdf63e8d9982b4d8a01ef5689425c93310ece950e517476e22af10f41a136e", + "https://deno.land/std@0.129.0/streams/conversion.ts": "712585bfa0172a97fb68dd46e784ae8ad59d11b88079d6a4ab098ff42e697d21", + "https://deno.land/std@0.129.0/testing/_diff.ts": "9d849cd6877694152e01775b2d93f9d6b7aef7e24bfe3bfafc4d7a1ac8e9f392", + "https://deno.land/std@0.129.0/testing/asserts.ts": "0a95d9e8076dd3e7f0eeb605a67c148078b4b11f4abcd5eef115b0361b0736a2", + "https://deno.land/x/compress@v0.4.5/deps.ts": "096395daebc7ed8a18f0484e4ffcc3a7f70e50946735f7df9611a7fcfd8272cc", + "https://deno.land/x/compress@v0.4.5/gzip/gzip.ts": "4bf22e9cd3368332928324dd9443ef72cabd05e9234e5a37dd7b3517d50e945e", + "https://deno.land/x/compress@v0.4.5/gzip/gzip_file.ts": "b044ec0df4266c084baa033a4ab5394882e44a86d09d5616636467dcb39c671d", + "https://deno.land/x/compress@v0.4.5/gzip/gzip_stream.ts": "6781cf0e47648e3e5631cba4cc2cd018a24935ce09fdaa86e0cabcf78b5012df", + "https://deno.land/x/compress@v0.4.5/gzip/mod.ts": "4ade8edbe01b54a84f289351e137ebdfc040a74cd616636770cf1724fbf522d1", + "https://deno.land/x/compress@v0.4.5/gzip/writer_gunzip.ts": "5aba34394820b835c414048ac2e15f52d443f1f773ebe61fd2517c938572d616", + "https://deno.land/x/compress@v0.4.5/gzip/writer_gzip.ts": "c7aad0c51ab4f5952c068088186339cfc79a2ee1e057d6e16731b1175f342645", + "https://deno.land/x/compress@v0.4.5/mod.ts": "ae8b15826334021583a5bd1978c63840f85156ea3635f5941bfc6733aad247e5", + "https://deno.land/x/compress@v0.4.5/tar/mod.ts": "6d9073005e678479908047cbe9e4716e484f80d1f2a1e15d3d6ac92213ffaeba", + "https://deno.land/x/compress@v0.4.5/tgz/mod.ts": "2fd4e99f26b57b0055d4d2f87721682304541ed1ca41bbb49c034d121f936f00", + "https://deno.land/x/compress@v0.4.5/utils/uint8.ts": "9c82e09c065f1f4bc648e3b14df441b43a7960fc7bdb29e9fb8d3a69c7e9d425", + "https://deno.land/x/compress@v0.4.5/zlib/deflate.ts": "e1e3b406dcc3e20021e53cde427b4b9ced752b72df820de73fec17c6e5ba999e", + "https://deno.land/x/compress@v0.4.5/zlib/inflate.ts": "618cc3dd25d202bf6b89d92f3ab2865e7495884cafce950638c77cbc1537aeb1", + "https://deno.land/x/compress@v0.4.5/zlib/mod.ts": "4dca9c1e934b7ab27f31c318abd7bfd39b09be96fd76ba27bd46f3a4e73b4ad0", + "https://deno.land/x/compress@v0.4.5/zlib/zlib/adler32.ts": "e34c7596d63a655755c4b0a44a40d4f9c1d1c4d3b891e5c1f3f840f8939e1940", + "https://deno.land/x/compress@v0.4.5/zlib/zlib/crc32.ts": "b9bc4adaf327d32585205d1176bd52f6453c06dd1040544611d4c869e638119c", + "https://deno.land/x/compress@v0.4.5/zlib/zlib/deflate.ts": "8d1dd88630279313e50deed4fe5feefe8128307cc48fa560e659b5234ab09d83", + "https://deno.land/x/compress@v0.4.5/zlib/zlib/gzheader.ts": "11e6da7383447aae9791308dc2350a809fa341a876a2da396b03a2a31408c20c", + "https://deno.land/x/compress@v0.4.5/zlib/zlib/inffast.ts": "282daf5ea16bb876d26e342f3c24fe1a8ec84640e713a970b02232955a853f86", + "https://deno.land/x/compress@v0.4.5/zlib/zlib/inflate.ts": "76751c1a5b18d70a929fa31ce4959db0bde1b9097bfa1b5ea3b4d1fba2ab92fa", + "https://deno.land/x/compress@v0.4.5/zlib/zlib/inftrees.ts": "8a6d765a5c42bf3b6990060cabbe52e88493f8ce6d082e6e35d97756914cfb8e", + "https://deno.land/x/compress@v0.4.5/zlib/zlib/messages.ts": "c82229bd67ccc3b6162f3aca1c5e7f936e546aa91ac9a9ac4fcfefc3a9dc5ac8", + "https://deno.land/x/compress@v0.4.5/zlib/zlib/status.ts": "5987864d2d43d59bbbfa2e6ef4d5a07284c1d10489cc5843ddf41ac547957ac3", + "https://deno.land/x/compress@v0.4.5/zlib/zlib/trees.ts": "6b65a767646e031e87e7b725ffad0c511fe701f393a01652e1e7ee8884f60fee", + "https://deno.land/x/compress@v0.4.5/zlib/zlib/zstream.ts": "c110fd5919235e317d64933852e24a1bba0126202be592e90e58f7b19315ad93", + "https://deno.land/x/crc32@v0.2.0/mod.ts": "de7a3fa2d4ef24b96fc21e1cc4d2d65d1d2b1dcea92f63960e3e11bfa82df0fa" + } +} From 9b94eef8cc6298134c17e08bbf7ecc2d58499750 Mon Sep 17 00:00:00 2001 From: Zixuan Chen Date: Mon, 19 May 2025 15:12:16 +0800 Subject: [PATCH 6/8] ci: env --- crates/loro-wasm/scripts/build.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/loro-wasm/scripts/build.ts b/crates/loro-wasm/scripts/build.ts index 63a44d30c..607e6fe7c 100644 --- a/crates/loro-wasm/scripts/build.ts +++ b/crates/loro-wasm/scripts/build.ts @@ -18,11 +18,10 @@ const TARGETS = ["bundler", "nodejs", "web"]; const startTime = performance.now(); const LoroWasmDir = path.resolve(__dirname, ".."); -// Check if running in CI -const isCI = Deno.env.get("CI") === "true"; const githubToken = Deno.env.get("GITHUB_TOKEN"); const githubEventPath = Deno.env.get("GITHUB_EVENT_PATH"); +console.log({ githubToken, githubEventPath }); console.log(LoroWasmDir); async function build() { await cargoBuild(); @@ -82,7 +81,7 @@ async function build() { console.log("Brotli size: ", brotliSize, "KB"); // Report sizes to PR if in CI - if (isCI && githubToken && githubEventPath) { + if (githubToken && githubEventPath) { try { // Parse GitHub event data const event = JSON.parse(await Deno.readTextFile(githubEventPath)); From c36792547d87a8017eee34a4967a8eab02b79eaf Mon Sep 17 00:00:00 2001 From: Zixuan Chen Date: Mon, 19 May 2025 15:31:22 +0800 Subject: [PATCH 7/8] ci: comment --- crates/loro-wasm/package.json | 1 + crates/loro-wasm/scripts/build.ts | 36 +++++++++++++----------------- crates/loro-wasm/scripts/deno.lock | 17 ++++++++++++++ pnpm-lock.yaml | 23 +++++++++++++++++++ 4 files changed, 57 insertions(+), 20 deletions(-) diff --git a/crates/loro-wasm/package.json b/crates/loro-wasm/package.json index e2118b283..86a273a75 100644 --- a/crates/loro-wasm/package.json +++ b/crates/loro-wasm/package.json @@ -36,6 +36,7 @@ "author": "", "license": "MIT", "devDependencies": { + "@actions/core": "^1.11.1", "@actions/github": "^6.0.1", "@rollup/plugin-alias": "^5.1.1", "@rollup/plugin-node-resolve": "^15.0.1", diff --git a/crates/loro-wasm/scripts/build.ts b/crates/loro-wasm/scripts/build.ts index 607e6fe7c..1d18bacee 100644 --- a/crates/loro-wasm/scripts/build.ts +++ b/crates/loro-wasm/scripts/build.ts @@ -1,13 +1,16 @@ import * as path from "https://deno.land/std@0.105.0/path/mod.ts"; import { gzip } from "https://deno.land/x/compress@v0.4.5/mod.ts"; import brotliPromise from "npm:brotli-wasm"; -import { getOctokit } from "npm:@actions/github"; +import { context, getOctokit } from "npm:@actions/github"; +import * as core from "npm:@actions/core"; const __dirname = path.dirname(path.fromFileUrl(import.meta.url)); // deno run -A build.ts debug // deno run -A build.ts release // deno run -A build.ts release web // deno run -A build.ts release nodejs + +const githubToken: string = core.getInput("github-token"); let profile = "dev"; let profileDir = "debug"; if (Deno.args[0] == "release") { @@ -18,10 +21,6 @@ const TARGETS = ["bundler", "nodejs", "web"]; const startTime = performance.now(); const LoroWasmDir = path.resolve(__dirname, ".."); -const githubToken = Deno.env.get("GITHUB_TOKEN"); -const githubEventPath = Deno.env.get("GITHUB_EVENT_PATH"); - -console.log({ githubToken, githubEventPath }); console.log(LoroWasmDir); async function build() { await cargoBuild(); @@ -81,15 +80,12 @@ async function build() { console.log("Brotli size: ", brotliSize, "KB"); // Report sizes to PR if in CI - if (githubToken && githubEventPath) { + if (githubToken) { try { + const prNumber: string = core.getInput("pr-number"); + console.log("Creating comment for PR", prNumber); // Parse GitHub event data - const event = JSON.parse(await Deno.readTextFile(githubEventPath)); - if (event.pull_request) { - const prNumber = event.pull_request.number; - const repo = event.repository.full_name; - const [owner, repoName] = repo.split("/"); - + if (prNumber !== "") { const commentBody = `## WASM Size Report - Original size: ${wasmSize} KB @@ -101,9 +97,9 @@ async function build() { // Find if we already have a comment with our marker const { data: comments } = await octokit.rest.issues.listComments({ - owner, - repo: repoName, - issue_number: prNumber, + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: parseInt(prNumber), }); const sizeReportMarker = ""; @@ -114,8 +110,8 @@ async function build() { if (existingComment) { // Update existing comment await octokit.rest.issues.updateComment({ - owner, - repo: repoName, + owner: context.repo.owner, + repo: context.repo.repo, comment_id: existingComment.id, body: commentBody, }); @@ -123,9 +119,9 @@ async function build() { } else { // Create new comment await octokit.rest.issues.createComment({ - owner, - repo: repoName, - issue_number: prNumber, + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: parseInt(prNumber), body: commentBody, }); console.log("Created new WASM size report comment"); diff --git a/crates/loro-wasm/scripts/deno.lock b/crates/loro-wasm/scripts/deno.lock index e4197300b..5681c181e 100644 --- a/crates/loro-wasm/scripts/deno.lock +++ b/crates/loro-wasm/scripts/deno.lock @@ -1,10 +1,24 @@ { "version": "5", "specifiers": { + "npm:@actions/core@*": "1.11.1", "npm:@actions/github@*": "6.0.1_@octokit+core@5.2.1", "npm:brotli-wasm@*": "3.0.1" }, "npm": { + "@actions/core@1.11.1": { + "integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==", + "dependencies": [ + "@actions/exec", + "@actions/http-client" + ] + }, + "@actions/exec@1.1.1": { + "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", + "dependencies": [ + "@actions/io" + ] + }, "@actions/github@6.0.1_@octokit+core@5.2.1": { "integrity": "sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw==", "dependencies": [ @@ -24,6 +38,9 @@ "undici" ] }, + "@actions/io@1.1.3": { + "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==" + }, "@fastify/busboy@2.1.1": { "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 91e94e2bf..d34b15a62 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: crates/loro-wasm: devDependencies: + '@actions/core': + specifier: ^1.11.1 + version: 1.11.1 '@actions/github': specifier: ^6.0.1 version: 6.0.1 @@ -123,12 +126,21 @@ importers: packages: + '@actions/core@1.11.1': + resolution: {integrity: sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==} + + '@actions/exec@1.1.1': + resolution: {integrity: sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==} + '@actions/github@6.0.1': resolution: {integrity: sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw==} '@actions/http-client@2.2.3': resolution: {integrity: sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==} + '@actions/io@1.1.3': + resolution: {integrity: sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==} + '@babel/code-frame@7.24.2': resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==} engines: {node: '>=6.9.0'} @@ -2640,6 +2652,15 @@ packages: snapshots: + '@actions/core@1.11.1': + dependencies: + '@actions/exec': 1.1.1 + '@actions/http-client': 2.2.3 + + '@actions/exec@1.1.1': + dependencies: + '@actions/io': 1.1.3 + '@actions/github@6.0.1': dependencies: '@actions/http-client': 2.2.3 @@ -2655,6 +2676,8 @@ snapshots: tunnel: 0.0.6 undici: 5.29.0 + '@actions/io@1.1.3': {} + '@babel/code-frame@7.24.2': dependencies: '@babel/highlight': 7.24.5 From cdfbe99f738f85fcf812076c5ebd8b57c985d063 Mon Sep 17 00:00:00 2001 From: Zixuan Chen Date: Mon, 19 May 2025 16:08:46 +0800 Subject: [PATCH 8/8] ci: comment --- .github/workflows/rust.yml | 4 +-- crates/loro-wasm/scripts/build.ts | 45 +++++++++++++++++++------------ 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1fc3c7e6e..4fed4ee99 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -40,7 +40,7 @@ jobs: with: version: "0.2.100" - uses: Swatinem/rust-cache@v2 - - name: Check + - name: Clippy Check run: cargo clippy --all-features -- -Dwarnings - name: Run rust tests - run: deno task test-all + run: GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} deno task test-all diff --git a/crates/loro-wasm/scripts/build.ts b/crates/loro-wasm/scripts/build.ts index 1d18bacee..f319b5a51 100644 --- a/crates/loro-wasm/scripts/build.ts +++ b/crates/loro-wasm/scripts/build.ts @@ -1,16 +1,13 @@ import * as path from "https://deno.land/std@0.105.0/path/mod.ts"; import { gzip } from "https://deno.land/x/compress@v0.4.5/mod.ts"; import brotliPromise from "npm:brotli-wasm"; -import { context, getOctokit } from "npm:@actions/github"; -import * as core from "npm:@actions/core"; +import { getOctokit } from "npm:@actions/github"; const __dirname = path.dirname(path.fromFileUrl(import.meta.url)); // deno run -A build.ts debug // deno run -A build.ts release // deno run -A build.ts release web // deno run -A build.ts release nodejs - -const githubToken: string = core.getInput("github-token"); let profile = "dev"; let profileDir = "debug"; if (Deno.args[0] == "release") { @@ -21,7 +18,16 @@ const TARGETS = ["bundler", "nodejs", "web"]; const startTime = performance.now(); const LoroWasmDir = path.resolve(__dirname, ".."); -console.log(LoroWasmDir); +// Check if running in CI +const isCI = Deno.env.get("CI") === "true"; +const githubToken = Deno.env.get("GITHUB_TOKEN"); +const githubEventPath = Deno.env.get("GITHUB_EVENT_PATH"); + +console.log({ + isCI, + githubToken: !!githubToken, + githubEventPath: githubEventPath, +}); async function build() { await cargoBuild(); const target = Deno.args[1]; @@ -80,12 +86,17 @@ async function build() { console.log("Brotli size: ", brotliSize, "KB"); // Report sizes to PR if in CI - if (githubToken) { + if (isCI && githubToken && githubEventPath) { + console.log("Creating comment for PR"); try { - const prNumber: string = core.getInput("pr-number"); - console.log("Creating comment for PR", prNumber); // Parse GitHub event data - if (prNumber !== "") { + const event = JSON.parse(await Deno.readTextFile(githubEventPath)); + console.log("event", event); + if (event.pull_request) { + const prNumber = event.pull_request.number; + const repo = event.repository.full_name; + const [owner, repoName] = repo.split("/"); + const commentBody = `## WASM Size Report - Original size: ${wasmSize} KB @@ -97,9 +108,9 @@ async function build() { // Find if we already have a comment with our marker const { data: comments } = await octokit.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: parseInt(prNumber), + owner, + repo: repoName, + issue_number: prNumber, }); const sizeReportMarker = ""; @@ -110,8 +121,8 @@ async function build() { if (existingComment) { // Update existing comment await octokit.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, + owner, + repo: repoName, comment_id: existingComment.id, body: commentBody, }); @@ -119,9 +130,9 @@ async function build() { } else { // Create new comment await octokit.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: parseInt(prNumber), + owner, + repo: repoName, + issue_number: prNumber, body: commentBody, }); console.log("Created new WASM size report comment");