From e54a5b61288b3378246483437b1fa78780ac568e Mon Sep 17 00:00:00 2001 From: LongYinan Date: Wed, 10 May 2023 17:16:48 +0800 Subject: [PATCH] feat(native): provide FSWatcher --- .gitignore | 4 + .taplo.toml | 9 + .vscode/settings.json | 1 - Cargo.lock | 756 +++++++++++++++++++++ Cargo.toml | 2 + apps/server/migrations/migration_lock.toml | 2 +- package.json | 6 +- packages/native/.gitignore | 198 +----- packages/native/Cargo.toml | 36 +- packages/native/__tests__/fs.spec.mts | 81 +++ packages/native/build.rs | 3 +- packages/native/index.d.ts | 50 +- packages/native/index.js | 92 +-- packages/native/package.json | 17 +- packages/native/src/block.rs | 216 ------ packages/native/src/dynamic_value.rs | 68 -- packages/native/src/fs.rs | 189 ++++++ packages/native/src/lib.rs | 13 +- packages/native/src/storage.rs | 125 ---- packages/native/src/workspace.rs | 84 --- packages/native/tsconfig.json | 12 + rustfmt.toml | 10 + yarn.lock | 29 +- 23 files changed, 1215 insertions(+), 788 deletions(-) create mode 100644 .taplo.toml create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 packages/native/__tests__/fs.spec.mts delete mode 100644 packages/native/src/block.rs delete mode 100644 packages/native/src/dynamic_value.rs create mode 100644 packages/native/src/fs.rs delete mode 100644 packages/native/src/storage.rs delete mode 100644 packages/native/src/workspace.rs create mode 100644 packages/native/tsconfig.json create mode 100644 rustfmt.toml diff --git a/.gitignore b/.gitignore index 463422dc44..ba02904ee8 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,7 @@ i18n-generated.ts # Cache .eslintcache next-env.d.ts + +# Rust +target +*.node diff --git a/.taplo.toml b/.taplo.toml new file mode 100644 index 0000000000..e884656992 --- /dev/null +++ b/.taplo.toml @@ -0,0 +1,9 @@ +exclude = ["node_modules/**/*.toml"] + +[[rule]] +keys = ["dependencies", "*-dependencies"] + +[rule.formatting] +align_entries = true +indent_tables = true +reorder_keys = true diff --git a/.vscode/settings.json b/.vscode/settings.json index a470203b8d..f64281320e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -26,7 +26,6 @@ "[toml]": { "editor.defaultFormatter": "tamasfe.even-better-toml" }, - "rust-analyzer.linkedProjects": ["packages/octobase-node/Cargo.toml"], "[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000000..3393e7865b --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,756 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "affine_octobase" +version = "0.0.0" +dependencies = [ + "anyhow", + "napi", + "napi-build", + "napi-derive", + "notify", + "parking_lot", + "serde", + "serde_json", + "tokio", + "uuid", +] + +[[package]] +name = "aho-corasick" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a6904aef64d73cf10ab17ebace7befb918b82164785cb89907993be7f83813" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ctor" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4056f63fce3b82d852c3da92b08ea59959890813a7f4ce9c0ff85b10cf301b" +dependencies = [ + "quote", + "syn 2.0.15", +] + +[[package]] +name = "filetime" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys 0.48.0", +] + +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + +[[package]] +name = "getrandom" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "kqueue" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8fc60ba15bf51257aa9807a48a61013db043fcf3a78cb0d916e8e396dcad98" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + +[[package]] +name = "libc" +version = "0.2.144" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.45.0", +] + +[[package]] +name = "napi" +version = "2.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49ac8112fe5998579b22e29903c7b277fc7f91c7860c0236f35792caf8156e18" +dependencies = [ + "anyhow", + "bitflags 2.2.1", + "ctor", + "napi-derive", + "napi-sys", + "once_cell", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "napi-build" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "882a73d9ef23e8dc2ebbffb6a6ae2ef467c0f18ac10711e4cc59c5485d41df0e" + +[[package]] +name = "napi-derive" +version = "2.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c47e0f395207c062e680a158f0624ec456c1dfb3c96a8cb888e0401506d50ae9" +dependencies = [ + "cfg-if", + "convert_case", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "napi-derive-backend" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a83afae5b4ba6f98ed6e33a52da343fdeb66474f1162a38cde5a3d46eb054e7" +dependencies = [ + "convert_case", + "once_cell", + "proc-macro2", + "quote", + "regex", + "semver", + "syn 1.0.109", +] + +[[package]] +name = "napi-sys" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "166b5ef52a3ab5575047a9fe8d4a030cdd0f63c96f071cd6907674453b07bae3" +dependencies = [ + "libloading", +] + +[[package]] +name = "notify" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58ea850aa68a06e48fdb069c0ec44d0d64c8dbffa49bf3b6f7f0a901fdea1ba9" +dependencies = [ + "bitflags 1.3.2", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "mio", + "serde", + "walkdir", + "windows-sys 0.42.0", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.162" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71b2f6e1ab5c2b98c05f0f35b236b22e8df7ead6ffbf51d7808da7f8817e7ab6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.162" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "serde_json" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tokio" +version = "1.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f" +dependencies = [ + "autocfg", + "num_cpus", + "pin-project-lite", + "windows-sys 0.48.0", +] + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "uuid" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dad5567ad0cf5b760e5665964bec1b47dfd077ba8a2544b513f3556d3d239a2" +dependencies = [ + "getrandom", + "rand", + "serde", +] + +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000..4246cf084e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["./packages/native"] diff --git a/apps/server/migrations/migration_lock.toml b/apps/server/migrations/migration_lock.toml index fbffa92c2b..99e4f20090 100644 --- a/apps/server/migrations/migration_lock.toml +++ b/apps/server/migrations/migration_lock.toml @@ -1,3 +1,3 @@ # Please do not edit this file manually # It should be added in your version-control system (i.e. Git) -provider = "postgresql" \ No newline at end of file +provider = "postgresql" diff --git a/package.json b/package.json index f24a47cbc2..131af4f7b3 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,10 @@ }, "lint-staged": { "*": "prettier --write --ignore-unknown --cache", - "*.{ts,tsx,mjs,js,jsx}": "eslint --cache --fix" + "*.{ts,tsx,mjs,js,jsx}": "eslint --cache --fix", + "*.toml": [ + "taplo format" + ] }, "devDependencies": { "@affine-test/kit": "workspace:*", @@ -48,6 +51,7 @@ "@magic-works/i18n-codegen": "^0.5.0", "@perfsee/sdk": "^1.6.0", "@playwright/test": "^1.33.0", + "@taplo/cli": "^0.5.2", "@testing-library/react": "^14.0.0", "@types/eslint": "^8.37.0", "@types/node": "^18.16.7", diff --git a/packages/native/.gitignore b/packages/native/.gitignore index fe393f513b..e454ccdc89 100644 --- a/packages/native/.gitignore +++ b/packages/native/.gitignore @@ -1,197 +1 @@ -# Created by https://www.toptal.com/developers/gitignore/api/node -# Edit at https://www.toptal.com/developers/gitignore?templates=node - -### Node ### -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.test - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# Next.js build output -.next - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# End of https://www.toptal.com/developers/gitignore/api/node - -# Created by https://www.toptal.com/developers/gitignore/api/macos -# Edit at https://www.toptal.com/developers/gitignore?templates=macos - -### macOS ### -# General -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two -Icon - - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -### macOS Patch ### -# iCloud generated files -*.icloud - -# End of https://www.toptal.com/developers/gitignore/api/macos - -# Created by https://www.toptal.com/developers/gitignore/api/windows -# Edit at https://www.toptal.com/developers/gitignore?templates=windows - -### Windows ### -# Windows thumbnail cache files -Thumbs.db -Thumbs.db:encryptable -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# End of https://www.toptal.com/developers/gitignore/api/windows - -#Added by cargo - -/target -Cargo.lock - -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/sdks -!.yarn/versions - -*.node +*.fixture diff --git a/packages/native/Cargo.toml b/packages/native/Cargo.toml index 338d63e4f2..ff2251fe19 100644 --- a/packages/native/Cargo.toml +++ b/packages/native/Cargo.toml @@ -7,23 +7,25 @@ version = "0.0.0" crate-type = ["cdylib"] [dependencies] +anyhow = "1" # Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix -napi = { version = "2.11.1", default-features = false, features = ["napi4", "tokio_rt"] } -napi-derive = "2.11.0" -jwst = { git = "https://github.com/toeverything/OctoBase", rev = "b701935", package = "jwst" } -jwst-storage = { git = "https://github.com/toeverything/OctoBase", rev = "b701935", package = "jwst-storage", features = [ "sqlite"] } -cloud-database = { git = "https://github.com/toeverything/OctoBase", rev = "b701935", package = "cloud-database", features = [ "sqlite"] } -jwst-rpc = { git = "https://github.com/toeverything/OctoBase", rev = "b701935", package = "jwst-rpc" } -lib0 = "0.16.3" -tokio = "1.24.2" -yrs = "0.16.3" -bytes = "1.3.0" -futures = "^0.3.25" +napi = { version = "2", default-features = false, features = [ + "napi4", + "tokio_rt", + "serde-json", + "error_anyhow", +] } +napi-derive = "2" +notify = { version = "5", features = ["serde"] } +parking_lot = "0.12" +serde = "1" +serde_json = "1" +tokio = "1" +uuid = { version = "1", default-features = false, features = [ + "serde", + "v4", + "fast-rng", +] } [build-dependencies] -napi-build = "2.0.1" - -[patch.crates-io] -rust-embed = { git = "https://github.com/pyrossh/rust-embed", rev = "7c0fc42" } -lib0 = { git = "https://github.com/toeverything/y-crdt", rev = "a3f7263" } -yrs = { git = "https://github.com/toeverything/y-crdt", rev = "a3f7263" } +napi-build = "2" diff --git a/packages/native/__tests__/fs.spec.mts b/packages/native/__tests__/fs.spec.mts new file mode 100644 index 0000000000..463eb8d081 --- /dev/null +++ b/packages/native/__tests__/fs.spec.mts @@ -0,0 +1,81 @@ +import assert, { doesNotThrow } from 'node:assert'; +import { promises as fs } from 'node:fs'; +import { join } from 'node:path'; +import { test } from 'node:test'; +import { fileURLToPath } from 'node:url'; + +import { lastValueFrom, Subject } from 'rxjs'; +import { v4 } from 'uuid'; + +import type { FSWatcher } from '../index'; +import { watch } from '../index.js'; + +test('fs watch', { concurrency: false }, async t => { + let watcher: FSWatcher; + let fixture: string; + t.beforeEach(async () => { + const fixtureName = `fs-${v4()}.fixture`; + fixture = join(fileURLToPath(import.meta.url), '..', fixtureName); + await fs.writeFile(fixture, '\n'); + watcher = watch(fixture); + }); + + t.afterEach(async () => { + watcher.close(); + await fs.unlink(fixture).catch(() => false); + }); + + await t.test('should watch without error', () => { + doesNotThrow(() => { + const subscription = watcher.subscribe(() => {}); + subscription.unsubscribe(); + }); + }); + + await t.test('should watch file change', () => { + return (async () => { + const defer = new Subject(); + const subscription = watcher.subscribe( + event => { + assert.deepEqual(event.paths, [fixture]); + subscription.unsubscribe(); + defer.next(); + defer.complete(); + }, + err => { + subscription.unsubscribe(); + defer.error(err); + } + ); + await fs.appendFile(fixture, 'test'); + return lastValueFrom(defer.asObservable()); + })(); + }); + + await t.test('should watch file delete', () => { + return (async () => { + const defer = new Subject(); + const subscription = watcher.subscribe( + event => { + if (event.type.remove) { + assert.deepEqual(event.paths, [fixture]); + assert.deepEqual(event.type, { + remove: { + kind: 'file', + }, + }); + } + subscription.unsubscribe(); + defer.next(); + defer.complete(); + }, + err => { + subscription.unsubscribe(); + defer.error(err); + } + ); + await fs.unlink(fixture); + return lastValueFrom(defer.asObservable()); + })(); + }); +}); diff --git a/packages/native/build.rs b/packages/native/build.rs index 1f866b6a3c..9626ce7fdc 100644 --- a/packages/native/build.rs +++ b/packages/native/build.rs @@ -1,5 +1,6 @@ extern crate napi_build; -fn main() { +fn main() -> Result<(), std::io::Error> { napi_build::setup(); + Ok(()) } diff --git a/packages/native/index.d.ts b/packages/native/index.d.ts index bcf7cb51d9..5ab29c2023 100644 --- a/packages/native/index.d.ts +++ b/packages/native/index.d.ts @@ -3,18 +3,42 @@ /* auto-generated by NAPI-RS */ -export class Storage { - constructor(path: string); - error(): string | null; - getBlob(workspaceId: string | undefined | null, id: string): Promise; - connect(workspaceId: string, remote: string): Workspace | null; - sync(workspaceId: string, remote: string): Workspace; +export interface WatchOptions { + recursive?: boolean; } -export class Workspace { - constructor(id: string); - id(): string; - clientId(): number; - search(query: string): string; - getSearchIndex(): Array; - setSearchIndex(fields: Array): boolean; +/** Watcher kind enumeration */ +export const enum WatcherKind { + /** inotify backend (linux) */ + Inotify = 'Inotify', + /** FS-Event backend (mac) */ + Fsevent = 'Fsevent', + /** KQueue backend (bsd,optionally mac) */ + Kqueue = 'Kqueue', + /** Polling based backend (fallback) */ + PollWatcher = 'PollWatcher', + /** Windows backend */ + ReadDirectoryChangesWatcher = 'ReadDirectoryChangesWatcher', + /** Fake watcher for testing */ + NullWatcher = 'NullWatcher', + Unknown = 'Unknown', +} +export function watch( + p: string, + options?: WatchOptions | undefined | null +): FSWatcher; +export class Subscription { + unsubscribe(): void; +} +export type FSWatcher = FsWatcher; +export class FsWatcher { + get kind(): WatcherKind; + toString(): string; + subscribe( + callback: (value: any) => any, + errorCallback?: ( + err: Error | null, + value: undefined + ) => any | undefined | null + ): Subscription; + close(): void; } diff --git a/packages/native/index.js b/packages/native/index.js index d6fedda9eb..70a9370cbc 100644 --- a/packages/native/index.js +++ b/packages/native/index.js @@ -36,13 +36,13 @@ switch (platform) { switch (arch) { case 'arm64': localFileExisted = existsSync( - join(__dirname, 'octobase.android-arm64.node') + join(__dirname, 'affine.android-arm64.node') ); try { if (localFileExisted) { - nativeBinding = require('./octobase.android-arm64.node'); + nativeBinding = require('./affine.android-arm64.node'); } else { - nativeBinding = require('@affine/octobase-node-android-arm64'); + nativeBinding = require('@affine/native-android-arm64'); } } catch (e) { loadError = e; @@ -50,13 +50,13 @@ switch (platform) { break; case 'arm': localFileExisted = existsSync( - join(__dirname, 'octobase.android-arm-eabi.node') + join(__dirname, 'affine.android-arm-eabi.node') ); try { if (localFileExisted) { - nativeBinding = require('./octobase.android-arm-eabi.node'); + nativeBinding = require('./affine.android-arm-eabi.node'); } else { - nativeBinding = require('@affine/octobase-node-android-arm-eabi'); + nativeBinding = require('@affine/native-android-arm-eabi'); } } catch (e) { loadError = e; @@ -70,13 +70,13 @@ switch (platform) { switch (arch) { case 'x64': localFileExisted = existsSync( - join(__dirname, 'octobase.win32-x64-msvc.node') + join(__dirname, 'affine.win32-x64-msvc.node') ); try { if (localFileExisted) { - nativeBinding = require('./octobase.win32-x64-msvc.node'); + nativeBinding = require('./affine.win32-x64-msvc.node'); } else { - nativeBinding = require('@affine/octobase-node-win32-x64-msvc'); + nativeBinding = require('@affine/native-win32-x64-msvc'); } } catch (e) { loadError = e; @@ -84,13 +84,13 @@ switch (platform) { break; case 'ia32': localFileExisted = existsSync( - join(__dirname, 'octobase.win32-ia32-msvc.node') + join(__dirname, 'affine.win32-ia32-msvc.node') ); try { if (localFileExisted) { - nativeBinding = require('./octobase.win32-ia32-msvc.node'); + nativeBinding = require('./affine.win32-ia32-msvc.node'); } else { - nativeBinding = require('@affine/octobase-node-win32-ia32-msvc'); + nativeBinding = require('@affine/native-win32-ia32-msvc'); } } catch (e) { loadError = e; @@ -98,13 +98,13 @@ switch (platform) { break; case 'arm64': localFileExisted = existsSync( - join(__dirname, 'octobase.win32-arm64-msvc.node') + join(__dirname, 'affine.win32-arm64-msvc.node') ); try { if (localFileExisted) { - nativeBinding = require('./octobase.win32-arm64-msvc.node'); + nativeBinding = require('./affine.win32-arm64-msvc.node'); } else { - nativeBinding = require('@affine/octobase-node-win32-arm64-msvc'); + nativeBinding = require('@affine/native-win32-arm64-msvc'); } } catch (e) { loadError = e; @@ -116,26 +116,26 @@ switch (platform) { break; case 'darwin': localFileExisted = existsSync( - join(__dirname, 'octobase.darwin-universal.node') + join(__dirname, 'affine.darwin-universal.node') ); try { if (localFileExisted) { - nativeBinding = require('./octobase.darwin-universal.node'); + nativeBinding = require('./affine.darwin-universal.node'); } else { - nativeBinding = require('@affine/octobase-node-darwin-universal'); + nativeBinding = require('@affine/native-darwin-universal'); } break; } catch {} switch (arch) { case 'x64': localFileExisted = existsSync( - join(__dirname, 'octobase.darwin-x64.node') + join(__dirname, 'affine.darwin-x64.node') ); try { if (localFileExisted) { - nativeBinding = require('./octobase.darwin-x64.node'); + nativeBinding = require('./affine.darwin-x64.node'); } else { - nativeBinding = require('@affine/octobase-node-darwin-x64'); + nativeBinding = require('@affine/native-darwin-x64'); } } catch (e) { loadError = e; @@ -143,13 +143,13 @@ switch (platform) { break; case 'arm64': localFileExisted = existsSync( - join(__dirname, 'octobase.darwin-arm64.node') + join(__dirname, 'affine.darwin-arm64.node') ); try { if (localFileExisted) { - nativeBinding = require('./octobase.darwin-arm64.node'); + nativeBinding = require('./affine.darwin-arm64.node'); } else { - nativeBinding = require('@affine/octobase-node-darwin-arm64'); + nativeBinding = require('@affine/native-darwin-arm64'); } } catch (e) { loadError = e; @@ -163,12 +163,12 @@ switch (platform) { if (arch !== 'x64') { throw new Error(`Unsupported architecture on FreeBSD: ${arch}`); } - localFileExisted = existsSync(join(__dirname, 'octobase.freebsd-x64.node')); + localFileExisted = existsSync(join(__dirname, 'affine.freebsd-x64.node')); try { if (localFileExisted) { - nativeBinding = require('./octobase.freebsd-x64.node'); + nativeBinding = require('./affine.freebsd-x64.node'); } else { - nativeBinding = require('@affine/octobase-node-freebsd-x64'); + nativeBinding = require('@affine/native-freebsd-x64'); } } catch (e) { loadError = e; @@ -179,26 +179,26 @@ switch (platform) { case 'x64': if (isMusl()) { localFileExisted = existsSync( - join(__dirname, 'octobase.linux-x64-musl.node') + join(__dirname, 'affine.linux-x64-musl.node') ); try { if (localFileExisted) { - nativeBinding = require('./octobase.linux-x64-musl.node'); + nativeBinding = require('./affine.linux-x64-musl.node'); } else { - nativeBinding = require('@affine/octobase-node-linux-x64-musl'); + nativeBinding = require('@affine/native-linux-x64-musl'); } } catch (e) { loadError = e; } } else { localFileExisted = existsSync( - join(__dirname, 'octobase.linux-x64-gnu.node') + join(__dirname, 'affine.linux-x64-gnu.node') ); try { if (localFileExisted) { - nativeBinding = require('./octobase.linux-x64-gnu.node'); + nativeBinding = require('./affine.linux-x64-gnu.node'); } else { - nativeBinding = require('@affine/octobase-node-linux-x64-gnu'); + nativeBinding = require('@affine/native-linux-x64-gnu'); } } catch (e) { loadError = e; @@ -208,26 +208,26 @@ switch (platform) { case 'arm64': if (isMusl()) { localFileExisted = existsSync( - join(__dirname, 'octobase.linux-arm64-musl.node') + join(__dirname, 'affine.linux-arm64-musl.node') ); try { if (localFileExisted) { - nativeBinding = require('./octobase.linux-arm64-musl.node'); + nativeBinding = require('./affine.linux-arm64-musl.node'); } else { - nativeBinding = require('@affine/octobase-node-linux-arm64-musl'); + nativeBinding = require('@affine/native-linux-arm64-musl'); } } catch (e) { loadError = e; } } else { localFileExisted = existsSync( - join(__dirname, 'octobase.linux-arm64-gnu.node') + join(__dirname, 'affine.linux-arm64-gnu.node') ); try { if (localFileExisted) { - nativeBinding = require('./octobase.linux-arm64-gnu.node'); + nativeBinding = require('./affine.linux-arm64-gnu.node'); } else { - nativeBinding = require('@affine/octobase-node-linux-arm64-gnu'); + nativeBinding = require('@affine/native-linux-arm64-gnu'); } } catch (e) { loadError = e; @@ -236,13 +236,13 @@ switch (platform) { break; case 'arm': localFileExisted = existsSync( - join(__dirname, 'octobase.linux-arm-gnueabihf.node') + join(__dirname, 'affine.linux-arm-gnueabihf.node') ); try { if (localFileExisted) { - nativeBinding = require('./octobase.linux-arm-gnueabihf.node'); + nativeBinding = require('./affine.linux-arm-gnueabihf.node'); } else { - nativeBinding = require('@affine/octobase-node-linux-arm-gnueabihf'); + nativeBinding = require('@affine/native-linux-arm-gnueabihf'); } } catch (e) { loadError = e; @@ -263,7 +263,9 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`); } -const { Storage, Workspace } = nativeBinding; +const { WatcherKind, Subscription, watch, FsWatcher } = nativeBinding; -module.exports.Storage = Storage; -module.exports.Workspace = Workspace; +module.exports.WatcherKind = WatcherKind; +module.exports.Subscription = Subscription; +module.exports.watch = watch; +module.exports.FsWatcher = FsWatcher; diff --git a/packages/native/package.json b/packages/native/package.json index 732265ae4a..d694f971d1 100644 --- a/packages/native/package.json +++ b/packages/native/package.json @@ -1,20 +1,28 @@ { - "name": "@affine/octobase-node", + "name": "@affine/native", "private": true, "main": "index.js", "types": "index.d.ts", "napi": { - "name": "octobase", + "name": "affine", "triples": { "additional": [ - "aarch64-apple-darwin" + "aarch64-apple-darwin", + "aarch64-unknown-linux-gnu", + "aarch64-pc-windows-msvc" ] } }, "license": "MIT", "devDependencies": { "@napi-rs/cli": "^2.15.2", - "@types/node": "^18.16.7" + "@types/node": "^18.16.7", + "@types/uuid": "^9.0.1", + "cross-env": "^7.0.3", + "rxjs": "^7.8.1", + "ts-node": "^10.9.1", + "typescript": "^5.0.4", + "uuid": "^9.0.0" }, "engines": { "node": ">= 10" @@ -24,6 +32,7 @@ "build": "napi build --platform --release", "build:debug": "napi build --platform", "universal": "napi universal", + "test": "cross-env TS_NODE_PROJECT=./tsconfig.json node --test --loader ts-node/esm --experimental-specifier-resolution=node ./__tests__/**/*.mts", "version": "napi version" }, "version": "0.6.0-canary.0" diff --git a/packages/native/src/block.rs b/packages/native/src/block.rs deleted file mode 100644 index 3e251c6a93..0000000000 --- a/packages/native/src/block.rs +++ /dev/null @@ -1,216 +0,0 @@ -use super::DynamicValue; -use jwst::{Block as JwstBlock, Workspace}; -use lib0::any::Any; - -#[napi()] -pub struct Block { - pub workspace: Workspace, - pub block: JwstBlock, -} - -#[napi()] -impl Block { - #[napi(constructor)] - pub fn new(workspace: Workspace, block: JwstBlock) -> Self { - Self { workspace, block } - } - - #[napi] - pub fn get(&self, key: String) -> Option { - self.workspace - .with_trx(|trx| self.block.get(&trx.trx, &key).map(DynamicValue::new)) - } - - #[napi] - pub fn children(&self) -> Vec { - self.workspace.with_trx(|trx| self.block.children(&trx.trx)) - } - - #[napi] - pub fn push_children(&self, block: &Block) { - self.workspace - .with_trx(|mut trx| self.block.push_children(&mut trx.trx, &block.block)); - } - - #[napi] - pub fn insert_children_at(&self, block: &Block, pos: u32) { - self.workspace.with_trx(|mut trx| { - self.block - .insert_children_at(&mut trx.trx, &block.block, pos) - }); - } - - #[napi] - pub fn insert_children_before(&self, block: &Block, reference: &str) { - self.workspace.with_trx(|mut trx| { - self.block - .insert_children_before(&mut trx.trx, &block.block, reference) - }); - } - - #[napi] - pub fn insert_children_after(&self, block: &Block, reference: &str) { - self.workspace.with_trx(|mut trx| { - self.block - .insert_children_after(&mut trx.trx, &block.block, reference) - }); - } - - #[napi] - pub fn remove_children(&self, block: &Block) { - self.workspace - .with_trx(|mut trx| self.block.remove_children(&mut trx.trx, &block.block)); - } - - #[napi] - pub fn exists_children(&self, block_id: &str) -> i32 { - self.workspace - .with_trx(|trx| self.block.exists_children(&trx.trx, block_id)) - .map(|i| i as i32) - .unwrap_or(-1) - } - - #[napi] - pub fn parent(&self) -> String { - self.workspace - .with_trx(|trx| self.block.parent(&trx.trx).unwrap()) - } - - #[napi] - pub fn updated(&self) -> u64 { - self.workspace.with_trx(|trx| self.block.updated(&trx.trx)) - } - - #[napi] - pub fn id(&self) -> String { - self.block.block_id() - } - - #[napi] - pub fn flavor(&self) -> String { - self.workspace.with_trx(|trx| self.block.flavor(&trx.trx)) - } - - #[napi] - pub fn version(&self) -> String { - self.workspace.with_trx(|trx| { - let [major, minor] = self.block.version(&trx.trx); - format!("{major}.{minor}") - }) - } - - #[napi] - pub fn created(&self) -> u64 { - self.workspace.with_trx(|trx| self.block.created(&trx.trx)) - } - - #[napi] - pub fn set_bool(&self, key: String, value: bool) { - self.workspace - .with_trx(|mut trx| self.block.set(&mut trx.trx, &key, value)); - } - - #[napi] - pub fn set_string(&self, key: String, value: String) { - self.workspace - .with_trx(|mut trx| self.block.set(&mut trx.trx, &key, value)); - } - - #[napi] - pub fn set_float(&self, key: String, value: f64) { - self.workspace - .with_trx(|mut trx| self.block.set(&mut trx.trx, &key, value)); - } - - #[napi] - pub fn set_integer(&self, key: String, value: i64) { - self.workspace - .with_trx(|mut trx| self.block.set(&mut trx.trx, &key, value)); - } - - #[napi] - pub fn set_null(&self, key: String) { - self.workspace - .with_trx(|mut trx| self.block.set(&mut trx.trx, &key, Any::Null)); - } - - #[napi] - pub fn is_bool(&self, key: String) -> bool { - self.workspace.with_trx(|trx| { - self.block - .get(&trx.trx, &key) - .map(|a| matches!(a, Any::Bool(_))) - .unwrap_or(false) - }) - } - - #[napi] - pub fn is_string(&self, key: String) -> bool { - self.workspace.with_trx(|trx| { - self.block - .get(&trx.trx, &key) - .map(|a| matches!(a, Any::String(_))) - .unwrap_or(false) - }) - } - - #[napi] - pub fn is_float(&self, key: String) -> bool { - self.workspace.with_trx(|trx| { - self.block - .get(&trx.trx, &key) - .map(|a| matches!(a, Any::Number(_))) - .unwrap_or(false) - }) - } - - #[napi] - pub fn is_integer(&self, key: String) -> bool { - self.workspace.with_trx(|trx| { - self.block - .get(&trx.trx, &key) - .map(|a| matches!(a, Any::BigInt(_))) - .unwrap_or(false) - }) - } - - #[napi] - pub fn get_bool(&self, key: String) -> Option { - self.workspace.with_trx(|trx| { - self.block.get(&trx.trx, &key).and_then(|a| match a { - Any::Bool(i) => Some(i.into()), - _ => None, - }) - }) - } - - #[napi] - pub fn get_string(&self, key: String) -> Option { - self.workspace.with_trx(|trx| { - self.block.get(&trx.trx, &key).and_then(|a| match a { - Any::String(i) => Some(i.into()), - _ => None, - }) - }) - } - - #[napi] - pub fn get_float(&self, key: String) -> Option { - self.workspace.with_trx(|trx| { - self.block.get(&trx.trx, &key).and_then(|a| match a { - Any::Number(i) => Some(i), - _ => None, - }) - }) - } - - #[napi] - pub fn get_integer(&self, key: String) -> Option { - self.workspace.with_trx(|trx| { - self.block.get(&trx.trx, &key).and_then(|a| match a { - Any::BigInt(i) => Some(i), - _ => None, - }) - }) - } -} diff --git a/packages/native/src/dynamic_value.rs b/packages/native/src/dynamic_value.rs deleted file mode 100644 index 37b5e62f35..0000000000 --- a/packages/native/src/dynamic_value.rs +++ /dev/null @@ -1,68 +0,0 @@ -use lib0::any::Any; -use std::collections::HashMap; - -pub type DynamicValueMap = HashMap; - -pub struct DynamicValue { - any: Any, -} - -impl DynamicValue { - pub fn new(any: Any) -> Self { - Self { any } - } - - pub fn as_bool(&self) -> Option { - match self.any { - Any::Bool(value) => Some(value), - _ => None, - } - } - - pub fn as_number(&self) -> Option { - match self.any { - Any::Number(value) => Some(value), - _ => None, - } - } - - pub fn as_int(&self) -> Option { - match self.any { - Any::BigInt(value) => Some(value), - _ => None, - } - } - - pub fn as_string(&self) -> Option { - match &self.any { - Any::String(value) => Some(value.to_string()), - _ => None, - } - } - - pub fn as_buffer(&self) -> Option> { - match &self.any { - Any::Buffer(value) => Some(value.to_vec()), - _ => None, - } - } - - pub fn as_array(&self) -> Option> { - match &self.any { - Any::Array(value) => Some(value.iter().map(|a| DynamicValue::new(a.clone())).collect()), - _ => None, - } - } - - pub fn as_map(&self) -> Option> { - match &self.any { - Any::Map(value) => Some( - value - .iter() - .map(|(key, value)| (key.clone(), DynamicValue::new(value.clone()))) - .collect(), - ), - _ => None, - } - } -} diff --git a/packages/native/src/fs.rs b/packages/native/src/fs.rs new file mode 100644 index 0000000000..f1b6f91332 --- /dev/null +++ b/packages/native/src/fs.rs @@ -0,0 +1,189 @@ +use std::{collections::HashMap, path::Path, sync::Arc}; + +use napi::{ + bindgen_prelude::{FromNapiValue, ToNapiValue}, + threadsafe_function::{ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode}, +}; +use napi_derive::napi; +use notify::{Event, RecommendedWatcher, RecursiveMode, Watcher}; +use parking_lot::Mutex; + +#[napi(object)] +#[derive(Default)] +pub struct WatchOptions { + pub recursive: Option, +} + +#[napi(string_enum)] +/// Watcher kind enumeration +pub enum WatcherKind { + /// inotify backend (linux) + Inotify, + /// FS-Event backend (mac) + Fsevent, + /// KQueue backend (bsd,optionally mac) + Kqueue, + /// Polling based backend (fallback) + PollWatcher, + /// Windows backend + ReadDirectoryChangesWatcher, + /// Fake watcher for testing + NullWatcher, + Unknown, +} + +impl From for WatcherKind { + fn from(value: notify::WatcherKind) -> Self { + match value { + notify::WatcherKind::Inotify => WatcherKind::Inotify, + notify::WatcherKind::Fsevent => WatcherKind::Fsevent, + notify::WatcherKind::Kqueue => WatcherKind::Kqueue, + notify::WatcherKind::PollWatcher => WatcherKind::PollWatcher, + notify::WatcherKind::ReadDirectoryChangesWatcher => WatcherKind::ReadDirectoryChangesWatcher, + notify::WatcherKind::NullWatcher => WatcherKind::NullWatcher, + _ => WatcherKind::Unknown, + } + } +} + +#[napi] +pub struct Subscription { + id: uuid::Uuid, + error_uuid: Option, + event_emitter: Arc>, +} + +#[napi] +impl Subscription { + #[napi] + #[allow(clippy::inherent_to_string)] + pub fn to_string(&self) -> String { + self.id.to_string() + } + + #[napi] + pub fn unsubscribe(&mut self) { + let mut event_emitter = self.event_emitter.lock(); + event_emitter.listeners.remove(&self.id); + if let Some(error_uuid) = &self.error_uuid { + event_emitter.error_callbacks.remove(error_uuid); + } + } +} + +#[napi] +pub fn watch(p: String, options: Option) -> Result { + let event_emitter = Arc::new(Mutex::new(EventEmitter { + listeners: Default::default(), + error_callbacks: Default::default(), + })); + let event_emitter_in_handler = event_emitter.clone(); + let mut watcher: RecommendedWatcher = + notify::recommended_watcher(move |res: notify::Result| { + event_emitter_in_handler.lock().on(res); + }) + .map_err(anyhow::Error::from)?; + + let options = options.unwrap_or_default(); + watcher + .watch( + Path::new(&p), + if options.recursive == Some(false) { + RecursiveMode::NonRecursive + } else { + RecursiveMode::Recursive + }, + ) + .map_err(anyhow::Error::from)?; + Ok(FSWatcher { + inner: watcher, + event_emitter, + }) +} + +#[napi] +pub struct FSWatcher { + inner: RecommendedWatcher, + event_emitter: Arc>, +} + +#[napi] +impl FSWatcher { + #[napi(getter)] + pub fn kind(&self) -> WatcherKind { + RecommendedWatcher::kind().into() + } + + #[napi] + pub fn to_string(&self) -> napi::Result { + Ok(format!("{:?}", self.inner)) + } + + #[napi] + pub fn subscribe( + &mut self, + callback: ThreadsafeFunction, + error_callback: Option>, + ) -> Subscription { + let uuid = uuid::Uuid::new_v4(); + let mut event_emitter = self.event_emitter.lock(); + event_emitter.listeners.insert(uuid, callback); + let mut error_uuid = None; + if let Some(error_callback) = error_callback { + let uuid = uuid::Uuid::new_v4(); + event_emitter.error_callbacks.insert(uuid, error_callback); + error_uuid = Some(uuid); + } + drop(event_emitter); + Subscription { + id: uuid, + error_uuid, + event_emitter: self.event_emitter.clone(), + } + } + + #[napi] + pub fn close(&mut self) -> napi::Result<()> { + // drop the previous watcher + self.inner = notify::recommended_watcher(|_| {}).map_err(anyhow::Error::from)?; + self.event_emitter.lock().stop(); + Ok(()) + } +} + +#[derive(Clone)] +struct EventEmitter { + listeners: HashMap>, + error_callbacks: HashMap>, +} + +impl EventEmitter { + fn on(&self, event: notify::Result) { + match event { + Ok(e) => match serde_json::value::to_value(e) { + Err(err) => { + let err: napi::Error = anyhow::Error::from(err).into(); + for on_error in self.error_callbacks.values() { + on_error.call(Err(err.clone()), ThreadsafeFunctionCallMode::NonBlocking); + } + } + Ok(v) => { + for on_event in self.listeners.values() { + on_event.call(v.clone(), ThreadsafeFunctionCallMode::NonBlocking); + } + } + }, + Err(err) => { + let err: napi::Error = anyhow::Error::from(err).into(); + for on_error in self.error_callbacks.values() { + on_error.call(Err(err.clone()), ThreadsafeFunctionCallMode::NonBlocking); + } + } + } + } + + fn stop(&mut self) { + self.listeners.clear(); + self.error_callbacks.clear(); + } +} diff --git a/packages/native/src/lib.rs b/packages/native/src/lib.rs index a3c4b9e6aa..d521fbd77e 100644 --- a/packages/native/src/lib.rs +++ b/packages/native/src/lib.rs @@ -1,12 +1 @@ -// mod block; -mod dynamic_value; -mod storage; -mod workspace; - -// pub use block::Block; -pub use dynamic_value::{DynamicValue, DynamicValueMap}; -pub use storage::Storage; -pub use workspace::Workspace; - -#[macro_use] -extern crate napi_derive; +pub mod fs; diff --git a/packages/native/src/storage.rs b/packages/native/src/storage.rs deleted file mode 100644 index d8233c04e2..0000000000 --- a/packages/native/src/storage.rs +++ /dev/null @@ -1,125 +0,0 @@ -use crate::Workspace; -use jwst::{error, info, BlobStorage, DocStorage}; -use jwst_rpc::start_client; -use jwst_storage::JwstStorage as AutoStorage; -use std::sync::Arc; -use tokio::{runtime::Runtime, sync::RwLock}; -use napi::bindgen_prelude::*; -use napi::{Error, Result, Status}; - -#[napi] -#[derive(Clone)] -pub struct Storage { - pub(crate) storage: Option>>, - pub(crate) error: Option, -} - -#[napi] -impl Storage { - #[napi(constructor)] - pub fn new(path: String) -> Self { - let rt = Runtime::new().unwrap(); - - // FIXME: do not use block_on - match rt.block_on(AutoStorage::new(&format!("sqlite:{path}?mode=rwc"))) { - Ok(pool) => Self { - storage: Some(Arc::new(RwLock::new(pool))), - error: None, - }, - Err(e) => Self { - storage: None, - error: Some(e.to_string()), - }, - } - } - - #[napi] - pub fn error(&self) -> Option { - self.error.clone() - } - - #[napi] - pub async fn get_blob(&self, workspace_id: Option, id: String) -> Result { - if let Some(storage) = &self.storage { - let storage_handle = storage.read().await; - let blobs = storage_handle.blobs(); - - let blob = blobs.get_blob(workspace_id.clone(), id.clone(), None).await.map_err(|e| { - Error::new( - Status::GenericFailure, - format!( - "Failed to get blob file {}/{} from storage, error: {}", - workspace_id.clone().unwrap_or_default().to_string(), - id, - e - ), - ) - })?; - - Ok(blob.into()) - } else { - return Err(Error::new( - Status::GenericFailure, - "Storage is not connected", - )); - } - } - - #[napi] - pub fn connect(&mut self, workspace_id: String, remote: String) -> Option { - match self.sync(workspace_id, remote) { - Ok(workspace) => Some(workspace), - Err(e) => { - error!("Failed to connect to workspace: {}", e); - self.error = Some(e.to_string()); - None - } - } - } - - #[napi] - pub fn sync(&self, workspace_id: String, remote: String) -> Result { - if let Some(storage) = &self.storage { - let rt = Runtime::new().unwrap(); - - // FIXME: do not use block_on - let mut workspace = rt - .block_on(async move { - let storage = storage.read().await; - - start_client(&storage, workspace_id, remote).await - }) - .map_err(|e| Error::new(Status::GenericFailure, e.to_string()))?; - - let (sub, workspace) = { - let id = workspace.id(); - let storage = self.storage.clone(); - let sub = workspace.observe(move |_, e| { - let id = id.clone(); - if let Some(storage) = storage.clone() { - let rt = Runtime::new().unwrap(); - info!("update: {:?}", &e.update); - if let Err(e) = rt.block_on(async move { - let storage = storage.write().await; - storage.docs().write_update(id, &e.update).await - }) { - error!("Failed to write update to storage: {}", e); - } - } - }); - - (sub, workspace) - }; - - Ok(Workspace { - workspace, - _sub: sub, - }) - } else { - Err(Error::new( - Status::GenericFailure, - "Storage is not connected", - )) - } - } -} diff --git a/packages/native/src/workspace.rs b/packages/native/src/workspace.rs deleted file mode 100644 index e0db27249d..0000000000 --- a/packages/native/src/workspace.rs +++ /dev/null @@ -1,84 +0,0 @@ -// use super::Block; -use jwst::Workspace as JwstWorkspace; -use yrs::UpdateSubscription; - - -#[napi()] -pub struct Workspace { - pub(crate) workspace: JwstWorkspace, - pub(crate) _sub: Option, -} - -#[napi()] -impl Workspace { - #[napi(constructor)] - pub fn new(id: String) -> Self { - Self { - workspace: JwstWorkspace::new(id), - _sub: None, - } - } - - #[napi] - pub fn id(&self) -> String { - self.workspace.id() - } - - #[napi] - pub fn client_id(&self) -> i64 { - self.workspace.client_id() as i64 - } - - // #[napi] - // pub fn get(&self, block_id: String) -> Option { - // let workspace = self.workspace.clone(); - // self.workspace.with_trx(|mut trx| { - // let block = trx - // .get_blocks() - // .get(&trx.trx, &block_id) - // .map(|b| Block::new(workspace, b)); - // drop(trx); - // block - // }) - // } - - // #[napi] - // pub fn create(&self, block_id: String, flavor: String) -> Block { - // let workspace = self.workspace.clone(); - // self.workspace.with_trx(|mut trx| { - // let block = Block::new( - // workspace, - // trx.get_blocks().create(&mut trx.trx, block_id, flavor), - // ); - // drop(trx); - // block - // }) - // } - - #[napi] - pub fn search(&self, query: String) -> String { - self.workspace.search_result(query) - } - - // #[napi] - // pub fn get_blocks_by_flavour(&self, flavour: &str) -> Vec { - // self.workspace - // .with_trx(|mut trx| trx.get_blocks().get_blocks_by_flavour(&trx.trx, flavour)) - // .iter() - // .map(|block| Block { - // workspace: self.workspace.clone(), - // block: block.clone(), - // }) - // .collect() - // } - - #[napi] - pub fn get_search_index(&self) -> Vec { - self.workspace.metadata().search_index - } - - #[napi] - pub fn set_search_index(&self, fields: Vec) -> bool { - self.workspace.set_search_index(fields) - } -} diff --git a/packages/native/tsconfig.json b/packages/native/tsconfig.json new file mode 100644 index 0000000000..792fc020fb --- /dev/null +++ b/packages/native/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": true, + "outDir": "lib" + }, + "include": ["index.d.ts", "__tests__/**/*.mts"], + "ts-node": { + "esm": true, + "experimentalSpecifierResolution": "node" + } +} diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000000..cb160519e2 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,10 @@ +max_width = 100 + +tab_spaces = 2 +hard_tabs = false + +format_strings = true +wrap_comments = true + +imports_granularity = "Crate" +group_imports = "StdExternalCrate" diff --git a/yarn.lock b/yarn.lock index 5c7cc985ad..a76b4a6e92 100644 --- a/yarn.lock +++ b/yarn.lock @@ -232,12 +232,18 @@ __metadata: languageName: unknown linkType: soft -"@affine/octobase-node@workspace:packages/native": +"@affine/native@workspace:packages/native": version: 0.0.0-use.local - resolution: "@affine/octobase-node@workspace:packages/native" + resolution: "@affine/native@workspace:packages/native" dependencies: "@napi-rs/cli": ^2.15.2 "@types/node": ^18.16.7 + "@types/uuid": ^9.0.1 + cross-env: ^7.0.3 + rxjs: ^7.8.1 + ts-node: ^10.9.1 + typescript: ^5.0.4 + uuid: ^9.0.0 languageName: unknown linkType: soft @@ -8143,6 +8149,15 @@ __metadata: languageName: node linkType: hard +"@taplo/cli@npm:^0.5.2": + version: 0.5.2 + resolution: "@taplo/cli@npm:0.5.2" + bin: + taplo: dist/cli.js + checksum: c2e0e584172bfee1cca6624bdb4470259179e232472fc7f4bbbd2e0127233039b9ace21a8d6b8d5081b157d9f046dc942ab27a634e23924b8c8a6096f1d04e27 + languageName: node + linkType: hard + "@testing-library/dom@npm:^8.3.0": version: 8.20.0 resolution: "@testing-library/dom@npm:8.20.0" @@ -9035,6 +9050,13 @@ __metadata: languageName: node linkType: hard +"@types/uuid@npm:^9.0.1": + version: 9.0.1 + resolution: "@types/uuid@npm:9.0.1" + checksum: c472b8a77cbeded4bc529220b8611afa39bd64677f507838f8083d8aac8033b1f88cb9ddaa2f8589e0dcd2317291d0f6e1379f82d5ceebd6f74f3b4825288e00 + languageName: node + linkType: hard + "@types/wait-on@npm:^5.2.0": version: 5.3.1 resolution: "@types/wait-on@npm:5.3.1" @@ -9748,6 +9770,7 @@ __metadata: "@magic-works/i18n-codegen": ^0.5.0 "@perfsee/sdk": ^1.6.0 "@playwright/test": ^1.33.0 + "@taplo/cli": ^0.5.2 "@testing-library/react": ^14.0.0 "@types/eslint": ^8.37.0 "@types/node": ^18.16.7 @@ -12195,7 +12218,7 @@ __metadata: languageName: node linkType: hard -"cross-env@npm:7.0.3": +"cross-env@npm:7.0.3, cross-env@npm:^7.0.3": version: 7.0.3 resolution: "cross-env@npm:7.0.3" dependencies: