From 7e6ead423227e32912ba69467bbde62111ed34a7 Mon Sep 17 00:00:00 2001 From: DarkSky <25152247+darkskygit@users.noreply.github.com> Date: Sat, 8 Nov 2025 23:07:16 +0800 Subject: [PATCH] feat: native doc reader (#13881) --- .cargo/config.toml | 9 +- .taplo.toml | 10 +- Cargo.lock | 3 + Cargo.toml | 223 +++---- packages/common/native/Cargo.toml | 69 ++- packages/common/native/fixtures/demo.ydoc | Bin 0 -> 38568 bytes .../common/native/fixtures/demo.ydoc.json | 567 ++++++++++++++++++ packages/common/native/src/doc_parser.rs | 528 ++++++++++++++++ packages/common/native/src/lib.rs | 3 + packages/common/y-octo/core/Cargo.toml | 4 +- .../android/App/gradle/libs.versions.toml | 198 +++--- packages/frontend/mobile-native/Cargo.toml | 10 +- packages/frontend/native/Cargo.toml | 35 +- packages/frontend/native/nbstore/Cargo.toml | 36 +- packages/frontend/native/sqlite_v1/Cargo.toml | 34 +- 15 files changed, 1460 insertions(+), 269 deletions(-) create mode 100644 packages/common/native/fixtures/demo.ydoc create mode 100644 packages/common/native/fixtures/demo.ydoc.json create mode 100644 packages/common/native/src/doc_parser.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index bbd24f91f6..6f84a73999 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -5,7 +5,14 @@ rustflags = ["-C", "target-feature=+crt-static"] [target.'cfg(target_os = "linux")'] rustflags = ["-C", "link-args=-Wl,--warn-unresolved-symbols"] [target.'cfg(target_os = "macos")'] -rustflags = ["-C", "link-args=-Wl,-undefined,dynamic_lookup,-no_fixup_chains", "-C", "link-args=-all_load", "-C", "link-args=-weak_framework ScreenCaptureKit"] +rustflags = [ + "-C", + "link-args=-Wl,-undefined,dynamic_lookup,-no_fixup_chains", + "-C", + "link-args=-all_load", + "-C", + "link-args=-weak_framework ScreenCaptureKit", +] # https://sourceware.org/bugzilla/show_bug.cgi?id=21032 # https://sourceware.org/bugzilla/show_bug.cgi?id=21031 # https://github.com/rust-lang/rust/issues/134820 diff --git a/.taplo.toml b/.taplo.toml index 5b26aabcd1..20c3b8ad70 100644 --- a/.taplo.toml +++ b/.taplo.toml @@ -1,7 +1,7 @@ -include = ["./*.toml", "./packages/**/*.toml"] +exclude = ["node_modules/**/*.toml", "target/**/*.toml"] +# https://taplo.tamasfe.dev/configuration/formatter-options.html [formatting] -align_entries = true -column_width = 180 -reorder_arrays = true -reorder_keys = true +align_entries = true +indent_tables = true +reorder_keys = true diff --git a/Cargo.lock b/Cargo.lock index e37bcdbc39..c472c8f0a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,6 +40,7 @@ dependencies = [ name = "affine_common" version = "0.1.0" dependencies = [ + "assert-json-diff", "cc", "chrono", "criterion2", @@ -50,6 +51,7 @@ dependencies = [ "rand 0.9.1", "rayon", "readability", + "serde", "serde_json", "sha3", "strum_macros", @@ -69,6 +71,7 @@ dependencies = [ "tree-sitter-scala", "tree-sitter-typescript", "url", + "y-octo", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f457d0fc7e..0039fe2e73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,109 +13,122 @@ members = [ ] resolver = "3" -[workspace.package] -edition = "2024" + [workspace.package] + edition = "2024" -[workspace.dependencies] -affine_common = { path = "./packages/common/native" } -affine_nbstore = { path = "./packages/frontend/native/nbstore" } -ahash = "0.8" -anyhow = "1" -arbitrary = { version = "1.3", features = ["derive"] } -assert-json-diff = "2.0" -async-lock = { version = "3.4.0", features = ["loom"] } -base64-simd = "0.8" -bitvec = "1.0" -block2 = "0.6" -byteorder = "1.5" -cpal = "0.15" -chrono = "0.4" -clap = { version = "4.4", features = ["derive"] } -core-foundation = "0.10" -coreaudio-rs = "0.12" -criterion = { version = "0.5", features = ["html_reports"] } -criterion2 = { version = "3", default-features = false } -crossbeam-channel = "0.5" -dispatch2 = "0.3" -docx-parser = { git = "https://github.com/toeverything/docx-parser" } -dotenvy = "0.15" -file-format = { version = "0.28", features = ["reader"] } -homedir = "0.3" -infer = { version = "0.19.0" } -lasso = { version = "0.7", features = ["multi-threaded"] } -lib0 = { version = "0.16", features = ["lib0-serde"] } -libc = "0.2" -log = "0.4" -loom = { version = "0.7", features = ["checkpoint"] } -mimalloc = "0.1" -mp4parse = "0.17" -nanoid = "0.4" -napi = { version = "3.0.0-beta.3", features = ["async", "chrono_date", "error_anyhow", "napi9", "serde"] } -napi-build = { version = "2" } -napi-derive = { version = "3.0.0-beta.3" } -nom = "8" -notify = { version = "8", features = ["serde"] } -objc2 = "0.6" -objc2-foundation = "0.3" -once_cell = "1" -ordered-float = "5" -parking_lot = "0.12" -path-ext = "0.1.2" -pdf-extract = { git = "https://github.com/toeverything/pdf-extract", branch = "darksky/improve-font-decoding" } -phf = { version = "0.11", features = ["macros"] } -proptest = "1.3" -proptest-derive = "0.5" -rand = "0.9" -rand_chacha = "0.9" -rand_distr = "0.5" -rayon = "1.10" -readability = { version = "0.3.0", default-features = false } -regex = "1.10" -rubato = "0.16" -screencapturekit = "0.3" -serde = "1" -serde_json = "1" -sha3 = "0.10" -smol_str = "0.3" -sqlx = { version = "0.8", default-features = false, features = ["chrono", "macros", "migrate", "runtime-tokio", "sqlite", "tls-rustls"] } -strum_macros = "0.27.0" -symphonia = { version = "0.5", features = ["all", "opt-simd"] } -text-splitter = "0.27" -thiserror = "2" -tiktoken-rs = "0.7" -tokio = "1.45" -tree-sitter = { version = "0.25" } -tree-sitter-c = { version = "0.24" } -tree-sitter-c-sharp = { version = "0.23" } -tree-sitter-cpp = { version = "0.23" } -tree-sitter-go = { version = "0.23" } -tree-sitter-java = { version = "0.23" } -tree-sitter-javascript = { version = "0.23" } -tree-sitter-kotlin-ng = { version = "1.1" } -tree-sitter-python = { version = "0.23" } -tree-sitter-rust = { version = "0.24" } -tree-sitter-scala = { version = "0.24" } -tree-sitter-typescript = { version = "0.23" } -uniffi = "0.29" -url = { version = "2.5" } -uuid = "1.8" -v_htmlescape = "0.15" -windows = { version = "0.61", features = [ - "Win32_Devices_FunctionDiscovery", - "Win32_UI_Shell_PropertiesSystem", - "Win32_Media_Audio", - "Win32_System_Variant", - "Win32_System_Com_StructuredStorage", - "Win32_System_Threading", - "Win32_System_ProcessStatus", - "Win32_Foundation", - "Win32_System_Com", - "Win32_System_Diagnostics_ToolHelp", -] } -windows-core = { version = "0.61" } -y-octo = { path = "./packages/common/y-octo/core" } -y-sync = { version = "0.4" } -yrs = "0.23.0" + [workspace.dependencies] + affine_common = { path = "./packages/common/native" } + affine_nbstore = { path = "./packages/frontend/native/nbstore" } + ahash = "0.8" + anyhow = "1" + arbitrary = { version = "1.3", features = ["derive"] } + assert-json-diff = "2.0" + async-lock = { version = "3.4.0", features = ["loom"] } + base64-simd = "0.8" + bitvec = "1.0" + block2 = "0.6" + byteorder = "1.5" + chrono = "0.4" + clap = { version = "4.4", features = ["derive"] } + core-foundation = "0.10" + coreaudio-rs = "0.12" + cpal = "0.15" + criterion = { version = "0.5", features = ["html_reports"] } + criterion2 = { version = "3", default-features = false } + crossbeam-channel = "0.5" + dispatch2 = "0.3" + docx-parser = { git = "https://github.com/toeverything/docx-parser" } + dotenvy = "0.15" + file-format = { version = "0.28", features = ["reader"] } + homedir = "0.3" + infer = { version = "0.19.0" } + lasso = { version = "0.7", features = ["multi-threaded"] } + lib0 = { version = "0.16", features = ["lib0-serde"] } + libc = "0.2" + log = "0.4" + loom = { version = "0.7", features = ["checkpoint"] } + mimalloc = "0.1" + mp4parse = "0.17" + nanoid = "0.4" + napi = { version = "3.0.0-beta.3", features = [ + "async", + "chrono_date", + "error_anyhow", + "napi9", + "serde", + ] } + napi-build = { version = "2" } + napi-derive = { version = "3.0.0-beta.3" } + nom = "8" + notify = { version = "8", features = ["serde"] } + objc2 = "0.6" + objc2-foundation = "0.3" + once_cell = "1" + ordered-float = "5" + parking_lot = "0.12" + path-ext = "0.1.2" + pdf-extract = { git = "https://github.com/toeverything/pdf-extract", branch = "darksky/improve-font-decoding" } + phf = { version = "0.11", features = ["macros"] } + proptest = "1.3" + proptest-derive = "0.5" + rand = "0.9" + rand_chacha = "0.9" + rand_distr = "0.5" + rayon = "1.10" + readability = { version = "0.3.0", default-features = false } + regex = "1.10" + rubato = "0.16" + screencapturekit = "0.3" + serde = "1" + serde_json = "1" + sha3 = "0.10" + smol_str = "0.3" + sqlx = { version = "0.8", default-features = false, features = [ + "chrono", + "macros", + "migrate", + "runtime-tokio", + "sqlite", + "tls-rustls", + ] } + strum_macros = "0.27.0" + symphonia = { version = "0.5", features = ["all", "opt-simd"] } + text-splitter = "0.27" + thiserror = "2" + tiktoken-rs = "0.7" + tokio = "1.45" + tree-sitter = { version = "0.25" } + tree-sitter-c = { version = "0.24" } + tree-sitter-c-sharp = { version = "0.23" } + tree-sitter-cpp = { version = "0.23" } + tree-sitter-go = { version = "0.23" } + tree-sitter-java = { version = "0.23" } + tree-sitter-javascript = { version = "0.23" } + tree-sitter-kotlin-ng = { version = "1.1" } + tree-sitter-python = { version = "0.23" } + tree-sitter-rust = { version = "0.24" } + tree-sitter-scala = { version = "0.24" } + tree-sitter-typescript = { version = "0.23" } + uniffi = "0.29" + url = { version = "2.5" } + uuid = "1.8" + v_htmlescape = "0.15" + windows = { version = "0.61", features = [ + "Win32_Devices_FunctionDiscovery", + "Win32_Foundation", + "Win32_Media_Audio", + "Win32_System_Com", + "Win32_System_Com_StructuredStorage", + "Win32_System_Diagnostics_ToolHelp", + "Win32_System_ProcessStatus", + "Win32_System_Threading", + "Win32_System_Variant", + "Win32_UI_Shell_PropertiesSystem", + ] } + windows-core = { version = "0.61" } + y-octo = { path = "./packages/common/y-octo/core" } + y-sync = { version = "0.4" } + yrs = "0.23.0" [profile.dev.package.sqlx-macros] opt-level = 3 @@ -126,6 +139,6 @@ lto = true opt-level = 3 strip = "symbols" -# android uniffi bindgen requires symbols -[profile.release.package.affine_mobile_native] -strip = "none" \ No newline at end of file + # android uniffi bindgen requires symbols + [profile.release.package.affine_mobile_native] + strip = "none" diff --git a/packages/common/native/Cargo.toml b/packages/common/native/Cargo.toml index 297ee91f06..e83fdaa36d 100644 --- a/packages/common/native/Cargo.toml +++ b/packages/common/native/Cargo.toml @@ -4,8 +4,22 @@ name = "affine_common" version = "0.1.0" [features] -default = [] -doc-loader = ["docx-parser", "infer", "path-ext", "pdf-extract", "readability", "serde_json", "strum_macros", "text-splitter", "thiserror", "tree-sitter", "url"] +default = ["hashcash"] +doc-loader = [ + "docx-parser", + "infer", + "path-ext", + "pdf-extract", + "readability", + "serde", + "serde_json", + "strum_macros", + "text-splitter", + "thiserror", + "tree-sitter", + "url", +] +hashcash = ["sha3", "rand"] tree-sitter = [ "cc", "dep:tree-sitter", @@ -21,34 +35,41 @@ tree-sitter = [ "dep:tree-sitter-scala", "dep:tree-sitter-typescript", ] +ydoc-loader = ["assert-json-diff", "y-octo"] [dependencies] chrono = { workspace = true } -rand = { workspace = true } -sha3 = { workspace = true } +rand = { workspace = true, optional = true } +sha3 = { workspace = true, optional = true } -docx-parser = { workspace = true, optional = true } -infer = { workspace = true, optional = true } -path-ext = { workspace = true, optional = true } -pdf-extract = { workspace = true, optional = true } -readability = { workspace = true, optional = true, default-features = false } -serde_json = { workspace = true, optional = true } -strum_macros = { workspace = true, optional = true } -text-splitter = { workspace = true, features = ["markdown", "tiktoken-rs"], optional = true } -thiserror = { workspace = true, optional = true } -tree-sitter = { workspace = true, optional = true } -tree-sitter-c = { workspace = true, optional = true } -tree-sitter-c-sharp = { workspace = true, optional = true } -tree-sitter-cpp = { workspace = true, optional = true } -tree-sitter-go = { workspace = true, optional = true } -tree-sitter-java = { workspace = true, optional = true } +assert-json-diff = { workspace = true, optional = true } +docx-parser = { workspace = true, optional = true } +infer = { workspace = true, optional = true } +path-ext = { workspace = true, optional = true } +pdf-extract = { workspace = true, optional = true } +readability = { workspace = true, optional = true, default-features = false } +serde = { workspace = true, optional = true, features = ["derive"] } +serde_json = { workspace = true, optional = true } +strum_macros = { workspace = true, optional = true } +text-splitter = { workspace = true, features = [ + "markdown", + "tiktoken-rs", +], optional = true } +thiserror = { workspace = true, optional = true } +tree-sitter = { workspace = true, optional = true } +tree-sitter-c = { workspace = true, optional = true } +tree-sitter-c-sharp = { workspace = true, optional = true } +tree-sitter-cpp = { workspace = true, optional = true } +tree-sitter-go = { workspace = true, optional = true } +tree-sitter-java = { workspace = true, optional = true } tree-sitter-javascript = { workspace = true, optional = true } -tree-sitter-kotlin-ng = { workspace = true, optional = true } -tree-sitter-python = { workspace = true, optional = true } -tree-sitter-rust = { workspace = true, optional = true } -tree-sitter-scala = { workspace = true, optional = true } +tree-sitter-kotlin-ng = { workspace = true, optional = true } +tree-sitter-python = { workspace = true, optional = true } +tree-sitter-rust = { workspace = true, optional = true } +tree-sitter-scala = { workspace = true, optional = true } tree-sitter-typescript = { workspace = true, optional = true } -url = { workspace = true, optional = true } +url = { workspace = true, optional = true } +y-octo = { workspace = true, optional = true } tiktoken-rs = { workspace = true } diff --git a/packages/common/native/fixtures/demo.ydoc b/packages/common/native/fixtures/demo.ydoc new file mode 100644 index 0000000000000000000000000000000000000000..66495a30c863e422325a857b942216d81dcd029f GIT binary patch literal 38568 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 literal 0 HcmV?d00001 diff --git a/packages/common/native/fixtures/demo.ydoc.json b/packages/common/native/fixtures/demo.ydoc.json new file mode 100644 index 0000000000..f6834ff1da --- /dev/null +++ b/packages/common/native/fixtures/demo.ydoc.json @@ -0,0 +1,567 @@ +{ + "blocks": [ + { + "block_id": "F_zl1z0ex6dSxM25ZBUWk", + "flavour": "affine:page", + "content": [ + "Write, Draw, Plan all at Once." + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": null, + "parent_block_id": null, + "additional": "{\"displayMode\":\"edgeless\"}" + }, + { + "block_id": "U8g2Edcy8rSmu_XUpYSTz", + "flavour": "affine:paragraph", + "content": [ + "AFFiNE is an open source all in one workspace, an operating system for all the building blocks of your team wiki, knowledge management and digital assets and a better alternative to Notion and Miro. " + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "omG6fe87r60dPxCtv_Odb", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"omG6fe87r60dPxCtv_Odb\"}" + }, + { + "block_id": "NxvF6M1Zo-TrUoJyFbMy4", + "flavour": "affine:paragraph", + "content": [ + "" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "omG6fe87r60dPxCtv_Odb", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"omG6fe87r60dPxCtv_Odb\"}" + }, + { + "block_id": "VHVvola5BtXnC7FMaGzE7", + "flavour": "affine:paragraph", + "content": [ + "You own your data, with no compromises" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "omG6fe87r60dPxCtv_Odb", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"omG6fe87r60dPxCtv_Odb\"}" + }, + { + "block_id": "Hvbw1lSZ7Bl-LMiOc_xFb", + "flavour": "affine:paragraph", + "content": [ + "Local-first & Real-time collaborative" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "omG6fe87r60dPxCtv_Odb", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"omG6fe87r60dPxCtv_Odb\"}" + }, + { + "block_id": "2N8pJ32byPEc7dlJv-0cC", + "flavour": "affine:paragraph", + "content": [ + "We love the idea proposed by Ink & Switch in the famous article about you owning your data, despite the cloud. Furthermore, AFFiNE is the first all-in-one workspace that keeps your data ownership with no compromises on real-time collaboration and editing experience." + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "omG6fe87r60dPxCtv_Odb", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"omG6fe87r60dPxCtv_Odb\"}" + }, + { + "block_id": "rysuvULJQgFPRmNB_iTk3", + "flavour": "affine:paragraph", + "content": [ + "AFFiNE is a local-first application upon CRDTs with real-time collaboration support. Your data is always stored locally while multiple nodes remain synced in real-time." + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "omG6fe87r60dPxCtv_Odb", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"omG6fe87r60dPxCtv_Odb\"}" + }, + { + "block_id": "zbvmiGhuxn0_tfJ_e4N2V", + "flavour": "affine:paragraph", + "content": [ + "" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "omG6fe87r60dPxCtv_Odb", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"omG6fe87r60dPxCtv_Odb\"}" + }, + { + "block_id": "CQeQaaREQwGDXg9oAng4q", + "flavour": "affine:paragraph", + "content": [ + "Blocks that assemble your next docs, tasks kanban or whiteboard" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "QqLfbOIINoAxapnB23E5t", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"QqLfbOIINoAxapnB23E5t\"}" + }, + { + "block_id": "RTtZAHZG4xoAXKKnEsRhB", + "flavour": "affine:paragraph", + "content": [ + "There is a large overlap of their atomic \"building blocks\" between these apps. They are neither open source nor have a plugin system like VS Code for contributors to customize. We want to have something that contains all the features we love and goes one step further. " + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "Fpxp7rZCad7-W51xCWta1", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"Fpxp7rZCad7-W51xCWta1\"}" + }, + { + "block_id": "c_KFpzTBHdioeoIsnLIjd", + "flavour": "affine:paragraph", + "content": [ + "We are building AFFiNE to be a fundamental open source platform that contains all the building blocks for docs, task management and visual collaboration, hoping you can shape your next workflow with us that can make your life better and also connect others, too." + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "Fpxp7rZCad7-W51xCWta1", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"Fpxp7rZCad7-W51xCWta1\"}" + }, + { + "block_id": "O6D7PxXwCbfCLp1_peft1", + "flavour": "affine:paragraph", + "content": [ + "If you want to learn more about the product design of AFFiNE, here goes the concepts:" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "Fpxp7rZCad7-W51xCWta1", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"Fpxp7rZCad7-W51xCWta1\"}" + }, + { + "block_id": "os84XIVFN6z6aN9tq16S5", + "flavour": "affine:paragraph", + "content": [ + "To Shape, not to adapt. AFFiNE is built for individuals & teams who care about their data, who refuse vendor lock-in, and who want to have control over their essential tools." + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "Fpxp7rZCad7-W51xCWta1", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"Fpxp7rZCad7-W51xCWta1\"}" + }, + { + "block_id": "8jk1eREgtKdhwtee0nym-", + "flavour": "affine:paragraph", + "content": [ + "A true canvas for blocks in any form" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "sfycWDfMpgKsE7s5qucWr", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"sfycWDfMpgKsE7s5qucWr\"}" + }, + { + "block_id": "kDyc-RntYCDWSSYFl4SSn", + "flavour": "affine:paragraph", + "content": [ + "Many editor apps claimed to be a canvas for productivity. Since the Mother of All Demos, Douglas Engelbart, a creative and programable digital workspace has been a pursuit and an ultimate mission for generations of tool makers. " + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "sfycWDfMpgKsE7s5qucWr", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"sfycWDfMpgKsE7s5qucWr\"}" + }, + { + "block_id": "k0e4IqfQ2PRmsXjzDQ-3T", + "flavour": "affine:paragraph", + "content": [ + "" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "sfycWDfMpgKsE7s5qucWr", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"sfycWDfMpgKsE7s5qucWr\"}" + }, + { + "block_id": "QR-4T-qYH3gTbbo_VKjU-", + "flavour": "affine:paragraph", + "content": [ + "\"We shape our tools and thereafter our tools shape us”. A lot of pioneers have inspired us a long the way, e.g.:" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "sfycWDfMpgKsE7s5qucWr", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"sfycWDfMpgKsE7s5qucWr\"}" + }, + { + "block_id": "nyEEsW01cCQVIr7yG9ywg", + "flavour": "affine:list", + "content": [ + "Quip & Notion with their great concept of \"everything is a block\"" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "sfycWDfMpgKsE7s5qucWr", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"sfycWDfMpgKsE7s5qucWr\"}" + }, + { + "block_id": "ZmeTH3oq8ON10pqV1pQ-n", + "flavour": "affine:list", + "content": [ + "Trello with their Kanban" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "sfycWDfMpgKsE7s5qucWr", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"sfycWDfMpgKsE7s5qucWr\"}" + }, + { + "block_id": "SbbdUF95MVoBTlxILHyUL", + "flavour": "affine:list", + "content": [ + "Airtable & Miro with their no-code programable datasheets" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "sfycWDfMpgKsE7s5qucWr", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"sfycWDfMpgKsE7s5qucWr\"}" + }, + { + "block_id": "fIQp-yM2v8Iar7kXAooXh", + "flavour": "affine:list", + "content": [ + "Miro & Whimiscal with their edgeless visual whiteboard" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "sfycWDfMpgKsE7s5qucWr", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"sfycWDfMpgKsE7s5qucWr\"}" + }, + { + "block_id": "2S9xQwALMBxbas1N6Eyf4", + "flavour": "affine:list", + "content": [ + "Remnote & Capacities with their object-based tag system" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "sfycWDfMpgKsE7s5qucWr", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"sfycWDfMpgKsE7s5qucWr\"}" + }, + { + "block_id": "C5xu77eIYukdKCu7h0Cjp", + "flavour": "affine:paragraph", + "content": [ + "For more details, please refer to our RoadMap" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "tPAicJ4WMxtBhys0Exlxq", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"tPAicJ4WMxtBhys0Exlxq\"}" + }, + { + "block_id": "2goqe7zWXhtKX-pLcYN-h", + "flavour": "affine:paragraph", + "content": [ + "Self Host" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "tPAicJ4WMxtBhys0Exlxq", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"tPAicJ4WMxtBhys0Exlxq\"}" + }, + { + "block_id": "cBud4or0twRvF36twhwqZ", + "flavour": "affine:paragraph", + "content": [ + "Self host AFFiNE" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "tPAicJ4WMxtBhys0Exlxq", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"tPAicJ4WMxtBhys0Exlxq\"}" + }, + { + "block_id": "FB07QqbHZhRqj43IEOzJP", + "flavour": "affine:database", + "content": [ + "Learning From", + "Title", + "Tag", + "Reference", + "Developers", + "AFFiNE" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "j9pzdPaGIVH627CPh_mik", + "additional": "{\"databaseName\":\"Learning From\",\"displayMode\":\"page\",\"noteBlockId\":\"j9pzdPaGIVH627CPh_mik\"}" + }, + { + "block_id": "YJ13TsE8mv5mxy2DIpMOD", + "flavour": "affine:paragraph", + "content": [ + "Affine Development" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:database", + "parent_block_id": "FB07QqbHZhRqj43IEOzJP", + "additional": "{\"databaseName\":\"Learning From\",\"displayMode\":\"page\",\"noteBlockId\":\"j9pzdPaGIVH627CPh_mik\"}" + }, + { + "block_id": "T_BOz9f-9uD5QjKABiciE", + "flavour": "affine:paragraph", + "content": [ + "For developers or installations guides, please go to AFFiNE Doc" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:database", + "parent_block_id": "FB07QqbHZhRqj43IEOzJP", + "additional": "{\"databaseName\":\"Learning From\",\"displayMode\":\"page\",\"noteBlockId\":\"j9pzdPaGIVH627CPh_mik\"}" + }, + { + "block_id": "yiMJW4ASGVUC7mly7ulce", + "flavour": "affine:paragraph", + "content": [ + "Quip & Notion with their great concept of \"everything is a block\"" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:database", + "parent_block_id": "FB07QqbHZhRqj43IEOzJP", + "additional": "{\"databaseName\":\"Learning From\",\"displayMode\":\"page\",\"noteBlockId\":\"j9pzdPaGIVH627CPh_mik\"}" + }, + { + "block_id": "qug1UtELLsBg2XwU1IJYK", + "flavour": "affine:paragraph", + "content": [ + "Trello with their Kanban" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:database", + "parent_block_id": "FB07QqbHZhRqj43IEOzJP", + "additional": "{\"databaseName\":\"Learning From\",\"displayMode\":\"page\",\"noteBlockId\":\"j9pzdPaGIVH627CPh_mik\"}" + }, + { + "block_id": "p86Sceg7KYFe1OgUUswCM", + "flavour": "affine:paragraph", + "content": [ + "Airtable & Miro with their no-code programable datasheets" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:database", + "parent_block_id": "FB07QqbHZhRqj43IEOzJP", + "additional": "{\"databaseName\":\"Learning From\",\"displayMode\":\"page\",\"noteBlockId\":\"j9pzdPaGIVH627CPh_mik\"}" + }, + { + "block_id": "ahyiL8RAajMoIoDekk2Je", + "flavour": "affine:paragraph", + "content": [ + "Miro & Whimiscal with their edgeless visual whiteboard" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:database", + "parent_block_id": "FB07QqbHZhRqj43IEOzJP", + "additional": "{\"databaseName\":\"Learning From\",\"displayMode\":\"page\",\"noteBlockId\":\"j9pzdPaGIVH627CPh_mik\"}" + }, + { + "block_id": "pKh_LNdwWD-yLDh-TVYAz", + "flavour": "affine:paragraph", + "content": [ + "Remnote & Capacities with their object-based tag system" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:database", + "parent_block_id": "FB07QqbHZhRqj43IEOzJP", + "additional": "{\"databaseName\":\"Learning From\",\"displayMode\":\"page\",\"noteBlockId\":\"j9pzdPaGIVH627CPh_mik\"}" + }, + { + "block_id": "b0ftuFAo7qDYRiI18TN6b", + "flavour": "affine:paragraph", + "content": [ + "Affine Development" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "wyVFKrydUAthKQEML84vE", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"wyVFKrydUAthKQEML84vE\"}" + }, + { + "block_id": "64rS_MtVI-f_MpE6qCbls", + "flavour": "affine:paragraph", + "content": [ + "For developer or installation guides, please go to AFFiNE Development" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "wyVFKrydUAthKQEML84vE", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"wyVFKrydUAthKQEML84vE\"}" + }, + { + "block_id": "7dotDICZwPWNvFXZSh-oF", + "flavour": "affine:paragraph", + "content": [ + "" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:note", + "parent_block_id": "wyVFKrydUAthKQEML84vE", + "additional": "{\"displayMode\":\"page\",\"noteBlockId\":\"wyVFKrydUAthKQEML84vE\"}" + }, + { + "block_id": "Jn4N8YdsFnqjBn-ae5zeR", + "flavour": "affine:surface", + "content": [ + "", + " ", + "AFFiNE ", + "Database Reference", + "Development", + "Related Articles", + "Self-host", + "What is AFFiNE", + "You can check these URLs to learn about AFFiNE" + ], + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:page", + "parent_block_id": "F_zl1z0ex6dSxM25ZBUWk", + "additional": "{\"displayMode\":\"edgeless\"}" + }, + { + "block_id": "YkZXI2iWRvQARfQPqspP_", + "flavour": "affine:bookmark", + "content": null, + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:surface", + "parent_block_id": "Jn4N8YdsFnqjBn-ae5zeR", + "additional": "{\"displayMode\":\"edgeless\"}" + }, + { + "block_id": "6xwR75KDkxvygEFWZfmhZ", + "flavour": "affine:bookmark", + "content": null, + "blob": null, + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:surface", + "parent_block_id": "Jn4N8YdsFnqjBn-ae5zeR", + "additional": "{\"displayMode\":\"edgeless\"}" + }, + { + "block_id": "AjY4OYuyp2536pBZlf-vD", + "flavour": "affine:image", + "content": [ + "" + ], + "blob": [ + "BFZk3c2ERp-sliRvA7MQ_p3NdkdCLt2Ze0DQ9i21dpA=" + ], + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:surface", + "parent_block_id": "Jn4N8YdsFnqjBn-ae5zeR", + "additional": "{\"displayMode\":\"edgeless\"}" + }, + { + "block_id": "ErTXTP8VqP5fabuB5FZHH", + "flavour": "affine:image", + "content": [ + "" + ], + "blob": [ + "HWvCItS78DzPGbwcuaGcfkpVDUvL98IvH5SIK8-AcL8=" + ], + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:surface", + "parent_block_id": "Jn4N8YdsFnqjBn-ae5zeR", + "additional": "{\"displayMode\":\"edgeless\"}" + }, + { + "block_id": "hMg8RXNlkD7XJS9b27fGA", + "flavour": "affine:image", + "content": [ + "" + ], + "blob": [ + "ZRKpsBoC88qEMmeiXKXqywfA1rLvWoLa5rpEh9x9Oj0=" + ], + "ref_doc_id": null, + "ref_info": null, + "parent_flavour": "affine:surface", + "parent_block_id": "Jn4N8YdsFnqjBn-ae5zeR", + "additional": "{\"displayMode\":\"edgeless\"}" + } + ], + "title": "Write, Draw, Plan all at Once.", + "summary": "AFFiNE is an open source all in one workspace, an operating system for all the building blocks of your team wiki, knowledge management and digital assets and a better alternative to Notion and Miro. You own your data, with no compromisesLocal-first & Real-time collaborativeWe love the idea proposed by Ink & Switch in the famous article about you owning your data, despite the cloud. Furthermore, AFFiNE is the first all-in-one workspace that keeps your data ownership with no compromises on real-time collaboration and editing experience.AFFiNE is a local-first application upon CRDTs with real-time collaboration support. Your data is always stored locally while multiple nodes remain synced in real-time.Blocks that assemble your next docs, tasks kanban or whiteboardThere is a large overlap of their atomic \"building blocks\" between these apps. They are neither open source nor have a plugin system like VS Code for contributors to customize. We want to have something that contains all the features we love and goes one step further. " +} \ No newline at end of file diff --git a/packages/common/native/src/doc_parser.rs b/packages/common/native/src/doc_parser.rs new file mode 100644 index 0000000000..e81c23bacf --- /dev/null +++ b/packages/common/native/src/doc_parser.rs @@ -0,0 +1,528 @@ +use std::collections::{HashMap, HashSet}; + +use serde::{Deserialize, Serialize}; +use serde_json::{Map as JsonMap, Value as JsonValue}; +use thiserror::Error; +use y_octo::{Any, DocOptions, JwstCodecError, Map, Value}; + +const SUMMARY_LIMIT: usize = 1000; +const PAGE_FLAVOUR: &str = "affine:page"; +const NOTE_FLAVOUR: &str = "affine:note"; + +const BOOKMARK_FLAVOURS: [&str; 5] = [ + "affine:bookmark", + "affine:embed-youtube", + "affine:embed-figma", + "affine:embed-github", + "affine:embed-loom", +]; + +#[derive(Debug, Clone)] +pub struct CrawlDocInput { + pub doc_bin: Vec, + pub root_doc_bin: Option>, + pub space_id: String, + pub doc_id: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BlockInfo { + pub block_id: String, + pub flavour: String, + pub content: Option>, + pub blob: Option>, + pub ref_doc_id: Option>, + pub ref_info: Option>, + pub parent_flavour: Option, + pub parent_block_id: Option, + pub additional: Option, +} + +impl BlockInfo { + fn base( + block_id: &str, + flavour: &str, + parent_flavour: Option<&String>, + parent_block_id: Option<&String>, + additional: Option, + ) -> Self { + Self { + block_id: block_id.to_string(), + flavour: flavour.to_string(), + content: None, + blob: None, + ref_doc_id: None, + ref_info: None, + parent_flavour: parent_flavour.cloned(), + parent_block_id: parent_block_id.cloned(), + additional, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CrawlResult { + pub blocks: Vec, + pub title: String, + pub summary: String, +} + +#[derive(Error, Debug, Serialize, Deserialize)] +pub enum ParseError { + #[error("doc_not_found")] + DocNotFound, + #[error("invalid_binary")] + InvalidBinary, + #[error("sqlite_error: {0}")] + SqliteError(String), + #[error("parser_error: {0}")] + ParserError(String), + #[error("unknown: {0}")] + Unknown(String), +} + +impl From for ParseError { + fn from(value: JwstCodecError) -> Self { + Self::ParserError(value.to_string()) + } +} + +pub fn parse_doc_from_binary(input: CrawlDocInput) -> Result { + let CrawlDocInput { + doc_bin, + root_doc_bin: _, + space_id: _, + doc_id, + } = input; + + if doc_bin.is_empty() { + return Err(ParseError::InvalidBinary); + } + + let mut doc = DocOptions::new().with_guid(doc_id.clone()).build(); + doc + .apply_update_from_binary_v1(&doc_bin) + .map_err(|_| ParseError::InvalidBinary)?; + + let blocks_map = doc.get_map("blocks")?; + if blocks_map.is_empty() { + return Err(ParseError::ParserError("blocks map is empty".into())); + } + + let mut block_pool: HashMap = HashMap::new(); + let mut parent_lookup: HashMap = HashMap::new(); + + for (_, value) in blocks_map.iter() { + if let Some(block_map) = value.to_map() { + if let Some(block_id) = get_block_id(&block_map) { + for child_id in collect_child_ids(&block_map) { + parent_lookup.insert(child_id, block_id.clone()); + } + block_pool.insert(block_id, block_map); + } + } + } + + let root_block_id = block_pool + .iter() + .find_map(|(id, block)| { + get_flavour(block) + .filter(|flavour| flavour == PAGE_FLAVOUR) + .map(|_| id.clone()) + }) + .ok_or_else(|| ParseError::ParserError("root block not found".into()))?; + + let mut queue: Vec<(Option, String)> = vec![(None, root_block_id.clone())]; + let mut visited: HashSet = HashSet::from([root_block_id.clone()]); + let mut blocks: Vec = Vec::with_capacity(block_pool.len()); + let mut doc_title = String::new(); + let mut summary = String::new(); + let mut summary_remaining = SUMMARY_LIMIT as isize; + + while let Some((parent_block_id, block_id)) = queue.pop() { + let block = match block_pool.get(&block_id) { + Some(block) => block, + None => continue, + }; + + let flavour = match get_flavour(block) { + Some(flavour) => flavour, + None => continue, + }; + + let parent_block = parent_block_id.as_ref().and_then(|id| block_pool.get(id)); + let parent_flavour = parent_block.and_then(get_flavour); + + let note_block = nearest_by_flavour(&block_id, NOTE_FLAVOUR, &parent_lookup, &block_pool); + let note_block_id = note_block.as_ref().and_then(get_block_id); + let display_mode = determine_display_mode(note_block.as_ref()); + + // enqueue children first to keep traversal order similar to JS implementation + let mut child_ids = collect_child_ids(block); + for child_id in child_ids.drain(..).rev() { + if visited.insert(child_id.clone()) { + queue.push((Some(block_id.clone()), child_id)); + } + } + + let build_block = |database_name: Option<&String>| { + BlockInfo::base( + &block_id, + &flavour, + parent_flavour.as_ref(), + parent_block_id.as_ref(), + compose_additional(&display_mode, note_block_id.as_ref(), database_name), + ) + }; + + if flavour == PAGE_FLAVOUR { + let title = get_string(block, "prop:title").unwrap_or_default(); + doc_title = title.clone(); + let mut info = build_block(None); + info.content = Some(vec![title]); + blocks.push(info); + continue; + } + + if matches!( + flavour.as_str(), + "affine:paragraph" | "affine:list" | "affine:code" + ) { + if let Some((text, text_len)) = text_content(block, "prop:text") { + let database_name = if flavour == "affine:paragraph" + && parent_flavour.as_deref() == Some("affine:database") + { + parent_block.and_then(|map| get_string(map, "prop:title")) + } else { + None + }; + + let mut info = build_block(database_name.as_ref()); + info.content = Some(vec![text.clone()]); + blocks.push(info); + append_summary(&mut summary, &mut summary_remaining, text_len, &text); + } + continue; + } + + if matches!( + flavour.as_str(), + "affine:embed-linked-doc" | "affine:embed-synced-doc" + ) { + if let Some(page_id) = get_string(block, "prop:pageId") { + let mut info = build_block(None); + info.ref_doc_id = Some(vec![page_id.clone()]); + if let Some(payload) = embed_ref_payload(block, &page_id) { + info.ref_info = Some(vec![payload]); + } + blocks.push(info); + } + continue; + } + + if flavour == "affine:attachment" { + if let Some(blob_id) = get_string(block, "prop:sourceId") { + let mut info = build_block(None); + info.blob = Some(vec![blob_id]); + info.content = Some(vec![get_string(block, "prop:name").unwrap_or_default()]); + blocks.push(info); + } + continue; + } + + if flavour == "affine:image" { + if let Some(blob_id) = get_string(block, "prop:sourceId") { + let mut info = build_block(None); + info.blob = Some(vec![blob_id]); + info.content = Some(vec![get_string(block, "prop:caption").unwrap_or_default()]); + blocks.push(info); + } + continue; + } + + if flavour == "affine:surface" { + let texts = gather_surface_texts(block); + let mut info = build_block(None); + info.content = Some(texts); + blocks.push(info); + continue; + } + + if flavour == "affine:database" { + let (texts, database_name) = gather_database_texts(block); + let mut info = BlockInfo::base( + &block_id, + &flavour, + parent_flavour.as_ref(), + parent_block_id.as_ref(), + compose_additional( + &display_mode, + note_block_id.as_ref(), + database_name.as_ref(), + ), + ); + info.content = Some(texts); + blocks.push(info); + continue; + } + + if flavour == "affine:latex" { + if let Some(content) = get_string(block, "prop:latex") { + let mut info = build_block(None); + info.content = Some(vec![content]); + blocks.push(info); + } + continue; + } + + if flavour == "affine:table" { + let contents = gather_table_contents(block); + let mut info = build_block(None); + info.content = Some(contents); + blocks.push(info); + continue; + } + + if BOOKMARK_FLAVOURS.contains(&flavour.as_str()) { + blocks.push(build_block(None)); + } + } + + if doc_title.is_empty() { + doc_title = "Untitled".into(); + } + + Ok(CrawlResult { + blocks, + title: doc_title, + summary, + }) +} + +fn collect_child_ids(block: &Map) -> Vec { + block + .get("sys:children") + .and_then(|value| value.to_array()) + .map(|array| { + array + .iter() + .filter_map(|value| value_to_string(&value)) + .collect::>() + }) + .unwrap_or_default() +} + +fn get_block_id(block: &Map) -> Option { + get_string(block, "sys:id") +} + +fn get_flavour(block: &Map) -> Option { + get_string(block, "sys:flavour") +} + +fn get_string(block: &Map, key: &str) -> Option { + block.get(key).and_then(|value| value_to_string(&value)) +} + +fn text_content(block: &Map, key: &str) -> Option<(String, usize)> { + block.get(key).and_then(|value| { + value.to_text().map(|text| { + let content = text.to_string(); + let len = text.len() as usize; + (content, len) + }) + }) +} + +fn nearest_by_flavour( + start: &str, + flavour: &str, + parent_lookup: &HashMap, + blocks: &HashMap, +) -> Option { + let mut cursor = Some(start.to_string()); + while let Some(node) = cursor { + if let Some(block) = blocks.get(&node) { + if get_flavour(block).as_deref() == Some(flavour) { + return Some(block.clone()); + } + } + cursor = parent_lookup.get(&node).cloned(); + } + None +} + +fn determine_display_mode(note_block: Option<&Map>) -> String { + match note_block.and_then(|block| get_string(block, "prop:displayMode")) { + Some(mode) if mode == "both" => "page".into(), + Some(mode) => mode, + None => "edgeless".into(), + } +} + +fn compose_additional( + display_mode: &str, + note_block_id: Option<&String>, + database_name: Option<&String>, +) -> Option { + let mut payload = JsonMap::new(); + payload.insert( + "displayMode".into(), + JsonValue::String(display_mode.to_string()), + ); + if let Some(note_id) = note_block_id { + payload.insert("noteBlockId".into(), JsonValue::String(note_id.clone())); + } + if let Some(name) = database_name { + payload.insert("databaseName".into(), JsonValue::String(name.clone())); + } + Some(JsonValue::Object(payload).to_string()) +} + +fn embed_ref_payload(block: &Map, page_id: &str) -> Option { + let mut payload = JsonMap::new(); + payload.insert("docId".into(), JsonValue::String(page_id.to_string())); + + if let Some(params_value) = block.get("prop:params") { + if let Ok(JsonValue::Object(params)) = serde_json::to_value(¶ms_value) { + for (key, value) in params.into_iter() { + payload.insert(key, value); + } + } + } + + Some(JsonValue::Object(payload).to_string()) +} + +fn gather_surface_texts(block: &Map) -> Vec { + let mut texts = Vec::new(); + let elements = match block.get("prop:elements").and_then(|value| value.to_map()) { + Some(map) => map, + None => return texts, + }; + + if elements + .get("type") + .and_then(|value| value_to_string(&value)) + .as_deref() + != Some("$blocksuite:internal:native$") + { + return texts; + } + + if let Some(value_map) = elements.get("value").and_then(|value| value.to_map()) { + for value in value_map.values() { + if let Some(element) = value.to_map() { + if let Some(text) = element.get("text").and_then(|value| value.to_text()) { + texts.push(text.to_string()); + } + } + } + } + + texts.sort(); + texts +} + +fn gather_database_texts(block: &Map) -> (Vec, Option) { + let mut texts = Vec::new(); + let database_title = get_string(block, "prop:title"); + if let Some(title) = &database_title { + texts.push(title.clone()); + } + + if let Some(columns) = block.get("prop:columns").and_then(|value| value.to_array()) { + for column_value in columns.iter() { + if let Some(column) = column_value.to_map() { + if let Some(name) = get_string(&column, "name") { + texts.push(name); + } + if let Some(data) = column.get("data").and_then(|value| value.to_map()) { + if let Some(options) = data.get("options").and_then(|value| value.to_array()) { + for option_value in options.iter() { + if let Some(option) = option_value.to_map() { + if let Some(value) = get_string(&option, "value") { + texts.push(value); + } + } + } + } + } + } + } + } + + (texts, database_title) +} + +fn gather_table_contents(block: &Map) -> Vec { + let mut contents = Vec::new(); + for key in block.keys() { + if key.starts_with("prop:cells.") && key.ends_with(".text") { + if let Some(value) = block.get(key).and_then(|value| value_to_string(&value)) { + if !value.is_empty() { + contents.push(value); + } + } + } + } + contents +} + +fn value_to_string(value: &Value) -> Option { + if let Some(text) = value.to_text() { + return Some(text.to_string()); + } + + if let Some(any) = value.to_any() { + return any_to_string(&any); + } + + None +} + +fn any_to_string(any: &Any) -> Option { + match any { + Any::String(value) => Some(value.to_string()), + Any::Integer(value) => Some(value.to_string()), + Any::Float32(value) => Some(value.0.to_string()), + Any::Float64(value) => Some(value.0.to_string()), + Any::BigInt64(value) => Some(value.to_string()), + Any::True => Some("true".into()), + Any::False => Some("false".into()), + Any::Null | Any::Undefined => None, + Any::Array(_) | Any::Object(_) | Any::Binary(_) => serde_json::to_string(any).ok(), + } +} + +fn append_summary(summary: &mut String, remaining: &mut isize, text_len: usize, text: &str) { + if *remaining > 0 { + summary.push_str(text); + *remaining -= text_len as isize; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_doc_from_binary() { + let json = include_bytes!("../fixtures/demo.ydoc.json"); + let input = CrawlDocInput { + doc_bin: include_bytes!("../fixtures/demo.ydoc").to_vec(), + root_doc_bin: None, + space_id: "o9WCLGyxkLxdULZ-f2B9V".to_string(), + doc_id: "dYpV7PPhk8amRkY5IAcVO".to_string(), + }; + + let result = parse_doc_from_binary(input).unwrap(); + let config = assert_json_diff::Config::new(assert_json_diff::CompareMode::Strict) + .numeric_mode(assert_json_diff::NumericMode::AssumeFloat); + assert_json_diff::assert_json_matches!( + serde_json::from_slice::(json).unwrap(), + serde_json::json!(result), + config + ); + } +} diff --git a/packages/common/native/src/lib.rs b/packages/common/native/src/lib.rs index d865fd7a21..771f7dfb0f 100644 --- a/packages/common/native/src/lib.rs +++ b/packages/common/native/src/lib.rs @@ -1,3 +1,6 @@ #[cfg(feature = "doc-loader")] pub mod doc_loader; +#[cfg(feature = "ydoc-loader")] +pub mod doc_parser; +#[cfg(feature = "hashcash")] pub mod hashcash; diff --git a/packages/common/y-octo/core/Cargo.toml b/packages/common/y-octo/core/Cargo.toml index c4ac1e9589..f3c43b3a65 100644 --- a/packages/common/y-octo/core/Cargo.toml +++ b/packages/common/y-octo/core/Cargo.toml @@ -8,8 +8,8 @@ authors = [ description = "High-performance and thread-safe CRDT implementation compatible with Yjs" edition = "2021" homepage = "https://github.com/toeverything/y-octo" -include = ["src/**/*", "benches/**/*", "bin/**/*", "LICENSE", "README.md"] -keywords = ["collaboration", "crdt", "crdts", "yjs", "yata"] +include = ["LICENSE", "README.md", "benches/**/*", "bin/**/*", "src/**/*"] +keywords = ["collaboration", "crdt", "crdts", "yata", "yjs"] license = "MIT" name = "y-octo" readme = "README.md" diff --git a/packages/frontend/apps/android/App/gradle/libs.versions.toml b/packages/frontend/apps/android/App/gradle/libs.versions.toml index 04d4af870c..d629f9ed8f 100644 --- a/packages/frontend/apps/android/App/gradle/libs.versions.toml +++ b/packages/frontend/apps/android/App/gradle/libs.versions.toml @@ -1,113 +1,113 @@ [versions] -android-gradle-plugin = "8.10.0" -androidx-activity-compose = "1.10.1" -androidx-appcompat = "1.7.0" -androidx-browser = "1.8.0" -androidx-compose-bom = "2025.05.00" -androidx-coordinatorlayout = "1.3.0" -androidx-core-ktx = "1.16.0" -androidx-core-splashscreen = "1.0.1" +android-gradle-plugin = "8.10.0" +androidx-activity-compose = "1.10.1" +androidx-appcompat = "1.7.0" +androidx-browser = "1.8.0" +androidx-compose-bom = "2025.05.00" +androidx-coordinatorlayout = "1.3.0" +androidx-core-ktx = "1.16.0" +androidx-core-splashscreen = "1.0.1" androidx-datastore-preferences = "1.2.0-alpha02" -androidx-espresso-core = "3.6.1" -androidx-junit = "1.2.1" -androidx-lifecycle-compose = "2.9.0" -androidx-material3 = "1.3.1" -androidx-navigation = "2.9.0" -apollo = "4.2.0" -apollo-kotlin-adapters = "0.0.6" +androidx-espresso-core = "3.6.1" +androidx-junit = "1.2.1" +androidx-lifecycle-compose = "2.9.0" +androidx-material3 = "1.3.1" +androidx-navigation = "2.9.0" +apollo = "4.2.0" +apollo-kotlin-adapters = "0.0.6" # @keep -compileSdk = "35" -firebase-bom = "33.13.0" -firebase-crashlytics = "3.0.3" -google-services = "4.4.2" -gradle-versions = "0.52.0" -hilt = "2.56.2" -hilt-ext = "1.2.0" -jna = "5.17.0" -junit = "4.13.2" -kotlin = "2.1.20" -kotlinx-coroutines = "1.10.2" -kotlinx-datetime = "0.6.2" +compileSdk = "35" +firebase-bom = "33.13.0" +firebase-crashlytics = "3.0.3" +google-services = "4.4.2" +gradle-versions = "0.52.0" +hilt = "2.56.2" +hilt-ext = "1.2.0" +jna = "5.17.0" +junit = "4.13.2" +kotlin = "2.1.20" +kotlinx-coroutines = "1.10.2" +kotlinx-datetime = "0.6.2" kotlinx-serialization-json = "1.8.1" -ksp = "2.1.20-2.0.1" +ksp = "2.1.20-2.0.1" # @keep -minSdk = "22" +minSdk = "22" mozilla-rust-android = "0.9.6" -okhttp-bom = "5.0.0-alpha.14" -richtext = "1.0.0-alpha02" +okhttp-bom = "5.0.0-alpha.14" +richtext = "1.0.0-alpha02" # @keep -targetSdk = "35" -timber = "5.0.1" +targetSdk = "35" +timber = "5.0.1" version-catalog-update = "1.0.0" [libraries] -android-gradle-plugin = { module = "com.android.tools.build:gradle", version.ref = "android-gradle-plugin" } -androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity-compose" } -androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" } -androidx-browser = { module = "androidx.browser:browser", version.ref = "androidx-browser" } -androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "androidx-compose-bom" } -androidx-compose-foundation-layout = { module = "androidx.compose.foundation:foundation-layout" } +android-gradle-plugin = { module = "com.android.tools.build:gradle", version.ref = "android-gradle-plugin" } +androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity-compose" } +androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" } +androidx-browser = { module = "androidx.browser:browser", version.ref = "androidx-browser" } +androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "androidx-compose-bom" } +androidx-compose-foundation-layout = { module = "androidx.compose.foundation:foundation-layout" } androidx-compose-material-icons-extended = { module = "androidx.compose.material:material-icons-extended" } -androidx-compose-material3 = { module = "androidx.compose.material3:material3" } -androidx-compose-runtime-livedata = { module = "androidx.compose.runtime:runtime-livedata" } -androidx-compose-ui-googlefonts = { module = "androidx.compose.ui:ui-text-google-fonts" } -androidx-compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest" } -androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" } -androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" } -androidx-compose-ui-util = { module = "androidx.compose.ui:ui-util" } -androidx-compose-ui-viewbinding = { module = "androidx.compose.ui:ui-viewbinding" } -androidx-coordinatorlayout = { module = "androidx.coordinatorlayout:coordinatorlayout", version.ref = "androidx-coordinatorlayout" } -androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-core-ktx" } -androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "androidx-core-splashscreen" } -androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "androidx-datastore-preferences" } -androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-espresso-core" } -androidx-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-junit" } -androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle-compose" } -androidx-lifecycle-viewModelCompose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle-compose" } -androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "androidx-navigation" } -androidx-navigation-fragment = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "androidx-navigation" } -androidx-navigation-ui-ktx = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "androidx-navigation" } -apollo-adapters-core = { module = "com.apollographql.adapters:apollo-adapters-core", version.ref = "apollo-kotlin-adapters" } -apollo-adapters-kotlinx-datetime = { module = "com.apollographql.adapters:apollo-adapters-kotlinx-datetime", version.ref = "apollo-kotlin-adapters" } -apollo-api = { module = "com.apollographql.apollo:apollo-api", version.ref = "apollo" } -apollo-runtime = { module = "com.apollographql.apollo:apollo-runtime", version.ref = "apollo" } -firebase-analytics = { module = "com.google.firebase:firebase-analytics" } -firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebase-bom" } -firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics" } -firebase-storage = { module = "com.google.firebase:firebase-storage" } -google-services = { module = "com.google.gms:google-services", version.ref = "google-services" } -hilt-android-core = { module = "com.google.dagger:hilt-android", version.ref = "hilt" } -hilt-android-testing = { module = "com.google.dagger:hilt-android-testing", version.ref = "hilt" } -hilt-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" } -hilt-ext-compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "hilt-ext" } -hilt-ext-work = { module = "androidx.hilt:hilt-work", version.ref = "hilt-ext" } -jna = { module = "net.java.dev.jna:jna", version.ref = "jna" } -junit = { module = "junit:junit", version.ref = "junit" } -kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" } -kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx-datetime" } -kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization-json" } -okhttp = { module = "com.squareup.okhttp3:okhttp" } -okhttp-bom = { module = "com.squareup.okhttp3:okhttp-bom", version.ref = "okhttp-bom" } -okhttp-coroutines = { module = "com.squareup.okhttp3:okhttp-coroutines" } -okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor" } -okhttp-sse = { module = "com.squareup.okhttp3:okhttp-sse" } -richtext-commonmark = { module = "com.halilibo.compose-richtext:richtext-commonmark", version.ref = "richtext" } -richtext-ui = { module = "com.halilibo.compose-richtext:richtext-ui", version.ref = "richtext" } -timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" } +androidx-compose-material3 = { module = "androidx.compose.material3:material3" } +androidx-compose-runtime-livedata = { module = "androidx.compose.runtime:runtime-livedata" } +androidx-compose-ui-googlefonts = { module = "androidx.compose.ui:ui-text-google-fonts" } +androidx-compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest" } +androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" } +androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" } +androidx-compose-ui-util = { module = "androidx.compose.ui:ui-util" } +androidx-compose-ui-viewbinding = { module = "androidx.compose.ui:ui-viewbinding" } +androidx-coordinatorlayout = { module = "androidx.coordinatorlayout:coordinatorlayout", version.ref = "androidx-coordinatorlayout" } +androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-core-ktx" } +androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "androidx-core-splashscreen" } +androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "androidx-datastore-preferences" } +androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-espresso-core" } +androidx-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-junit" } +androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle-compose" } +androidx-lifecycle-viewModelCompose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle-compose" } +androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "androidx-navigation" } +androidx-navigation-fragment = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "androidx-navigation" } +androidx-navigation-ui-ktx = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "androidx-navigation" } +apollo-adapters-core = { module = "com.apollographql.adapters:apollo-adapters-core", version.ref = "apollo-kotlin-adapters" } +apollo-adapters-kotlinx-datetime = { module = "com.apollographql.adapters:apollo-adapters-kotlinx-datetime", version.ref = "apollo-kotlin-adapters" } +apollo-api = { module = "com.apollographql.apollo:apollo-api", version.ref = "apollo" } +apollo-runtime = { module = "com.apollographql.apollo:apollo-runtime", version.ref = "apollo" } +firebase-analytics = { module = "com.google.firebase:firebase-analytics" } +firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebase-bom" } +firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics" } +firebase-storage = { module = "com.google.firebase:firebase-storage" } +google-services = { module = "com.google.gms:google-services", version.ref = "google-services" } +hilt-android-core = { module = "com.google.dagger:hilt-android", version.ref = "hilt" } +hilt-android-testing = { module = "com.google.dagger:hilt-android-testing", version.ref = "hilt" } +hilt-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" } +hilt-ext-compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "hilt-ext" } +hilt-ext-work = { module = "androidx.hilt:hilt-work", version.ref = "hilt-ext" } +jna = { module = "net.java.dev.jna:jna", version.ref = "jna" } +junit = { module = "junit:junit", version.ref = "junit" } +kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" } +kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx-datetime" } +kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization-json" } +okhttp = { module = "com.squareup.okhttp3:okhttp" } +okhttp-bom = { module = "com.squareup.okhttp3:okhttp-bom", version.ref = "okhttp-bom" } +okhttp-coroutines = { module = "com.squareup.okhttp3:okhttp-coroutines" } +okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor" } +okhttp-sse = { module = "com.squareup.okhttp3:okhttp-sse" } +richtext-commonmark = { module = "com.halilibo.compose-richtext:richtext-commonmark", version.ref = "richtext" } +richtext-ui = { module = "com.halilibo.compose-richtext:richtext-ui", version.ref = "richtext" } +timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" } [plugins] -android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" } -android-library = { id = "com.android.library", version.ref = "android-gradle-plugin" } -apollo-android = { id = "com.apollographql.apollo", version.ref = "apollo" } -compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } -firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebase-crashlytics" } -google-service = { id = "com.google.gms.google-services", version.ref = "google-services" } -gradle-versions = { id = "com.github.ben-manes.versions", version.ref = "gradle-versions" } -hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } -jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } -kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" } -kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } -ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } -rust-android = { id = "org.mozilla.rust-android-gradle.rust-android", version.ref = "mozilla-rust-android" } +android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" } +android-library = { id = "com.android.library", version.ref = "android-gradle-plugin" } +apollo-android = { id = "com.apollographql.apollo", version.ref = "apollo" } +compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } +firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebase-crashlytics" } +google-service = { id = "com.google.gms.google-services", version.ref = "google-services" } +gradle-versions = { id = "com.github.ben-manes.versions", version.ref = "gradle-versions" } +hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } +jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" } +kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } +ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } +rust-android = { id = "org.mozilla.rust-android-gradle.rust-android", version.ref = "mozilla-rust-android" } version-catalog-update = { id = "nl.littlerobots.version-catalog-update", version.ref = "version-catalog-update" } diff --git a/packages/frontend/mobile-native/Cargo.toml b/packages/frontend/mobile-native/Cargo.toml index 634195df86..7562bd8752 100644 --- a/packages/frontend/mobile-native/Cargo.toml +++ b/packages/frontend/mobile-native/Cargo.toml @@ -25,8 +25,14 @@ thiserror = { workspace = true } uniffi = { workspace = true, features = ["cli", "tokio"] } [target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies] -objc2 = { workspace = true } -objc2-foundation = { workspace = true, features = ["NSArray", "NSFileManager", "NSPathUtilities", "NSString", "NSURL"] } +objc2 = { workspace = true } +objc2-foundation = { workspace = true, features = [ + "NSArray", + "NSFileManager", + "NSPathUtilities", + "NSString", + "NSURL", +] } [target.'cfg(not(any(target_os = "ios", target_os = "macos")))'.dependencies] homedir = { workspace = true } diff --git a/packages/frontend/native/Cargo.toml b/packages/frontend/native/Cargo.toml index 1c5011a911..978fa2ae11 100644 --- a/packages/frontend/native/Cargo.toml +++ b/packages/frontend/native/Cargo.toml @@ -7,17 +7,32 @@ version = "0.0.0" crate-type = ["cdylib", "rlib"] [dependencies] -affine_common = { workspace = true } +affine_common = { workspace = true } affine_media_capture = { path = "./media_capture" } -affine_nbstore = { path = "./nbstore" } -affine_sqlite_v1 = { path = "./sqlite_v1" } -napi = { workspace = true } -napi-derive = { workspace = true } -once_cell = { workspace = true } -sqlx = { workspace = true, default-features = false, features = ["chrono", "macros", "migrate", "runtime-tokio", "sqlite", "tls-rustls"] } -tokio = { workspace = true, features = ["full"] } +affine_nbstore = { path = "./nbstore" } +affine_sqlite_v1 = { path = "./sqlite_v1" } +napi = { workspace = true } +napi-derive = { workspace = true } +once_cell = { workspace = true } +sqlx = { workspace = true, default-features = false, features = [ + "chrono", + "macros", + "migrate", + "runtime-tokio", + "sqlite", + "tls-rustls", +] } +tokio = { workspace = true, features = ["full"] } [build-dependencies] napi-build = { workspace = true } -sqlx = { workspace = true, default-features = false, features = ["chrono", "json", "macros", "migrate", "runtime-tokio", "sqlite", "tls-rustls"] } -tokio = { workspace = true, features = ["full"] } +sqlx = { workspace = true, default-features = false, features = [ + "chrono", + "json", + "macros", + "migrate", + "runtime-tokio", + "sqlite", + "tls-rustls", +] } +tokio = { workspace = true, features = ["full"] } diff --git a/packages/frontend/native/nbstore/Cargo.toml b/packages/frontend/native/nbstore/Cargo.toml index aeaa701943..1402c9585a 100644 --- a/packages/frontend/native/nbstore/Cargo.toml +++ b/packages/frontend/native/nbstore/Cargo.toml @@ -11,20 +11,34 @@ use-as-lib = ["napi-derive/noop", "napi/noop"] [dependencies] affine_schema = { path = "../schema" } -anyhow = { workspace = true } -chrono = { workspace = true } -napi = { workspace = true } -napi-derive = { workspace = true } -sqlx = { workspace = true, default-features = false, features = ["chrono", "macros", "migrate", "runtime-tokio", "sqlite", "tls-rustls"] } -thiserror = { workspace = true } -tokio = { workspace = true, features = ["full"] } +anyhow = { workspace = true } +chrono = { workspace = true } +napi = { workspace = true } +napi-derive = { workspace = true } +sqlx = { workspace = true, default-features = false, features = [ + "chrono", + "macros", + "migrate", + "runtime-tokio", + "sqlite", + "tls-rustls", +] } +thiserror = { workspace = true } +tokio = { workspace = true, features = ["full"] } [target.'cfg(any(target_os = "ios", target_os = "android"))'.dependencies] uniffi = { workspace = true } [build-dependencies] affine_schema = { path = "../schema" } -dotenvy = { workspace = true } -napi-build = { workspace = true } -sqlx = { workspace = true, default-features = false, features = ["chrono", "json", "macros", "migrate", "runtime-tokio", "sqlite", "tls-rustls"] } -tokio = { workspace = true, features = ["full"] } +dotenvy = { workspace = true } +napi-build = { workspace = true } +sqlx = { workspace = true, default-features = false, features = [ + "chrono", + "macros", + "migrate", + "runtime-tokio", + "sqlite", + "tls-rustls", +] } +tokio = { workspace = true, features = ["full"] } diff --git a/packages/frontend/native/sqlite_v1/Cargo.toml b/packages/frontend/native/sqlite_v1/Cargo.toml index b6d1cd6555..9085d7dcbc 100644 --- a/packages/frontend/native/sqlite_v1/Cargo.toml +++ b/packages/frontend/native/sqlite_v1/Cargo.toml @@ -8,16 +8,30 @@ crate-type = ["cdylib", "rlib"] [dependencies] affine_schema = { path = "../schema" } -anyhow = { workspace = true } -chrono = { workspace = true } -napi = { workspace = true } -napi-derive = { workspace = true } -sqlx = { workspace = true, default-features = false, features = ["chrono", "macros", "migrate", "runtime-tokio", "sqlite", "tls-rustls"] } -tokio = { workspace = true, features = ["full"] } +anyhow = { workspace = true } +chrono = { workspace = true } +napi = { workspace = true } +napi-derive = { workspace = true } +sqlx = { workspace = true, default-features = false, features = [ + "chrono", + "macros", + "migrate", + "runtime-tokio", + "sqlite", + "tls-rustls", +] } +tokio = { workspace = true, features = ["full"] } [build-dependencies] affine_schema = { path = "../schema" } -dotenvy = { workspace = true } -napi-build = { workspace = true } -sqlx = { workspace = true, default-features = false, features = ["chrono", "json", "macros", "migrate", "runtime-tokio", "sqlite", "tls-rustls"] } -tokio = { workspace = true, features = ["full"] } +dotenvy = { workspace = true } +napi-build = { workspace = true } +sqlx = { workspace = true, default-features = false, features = [ + "chrono", + "macros", + "migrate", + "runtime-tokio", + "sqlite", + "tls-rustls", +] } +tokio = { workspace = true, features = ["full"] }