diff --git a/.gitignore b/.gitignore index 149320b..c7168c9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ dist/ typings/ +.vscode/ diff --git a/.gitmodules b/.gitmodules index c9f05a1..0303c62 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,3 +5,7 @@ path = test/wasi-testsuite url = https://github.com/WebAssembly/wasi-testsuite branch = prod/testsuite-base +[submodule "threads/examples/wasi_multi_threads_rustc/rust_wasm"] + path = threads/examples/wasi_multi_threads_rustc/rust_wasm + url = https://github.com/oligamiq/rust_wasm + sharrow = true diff --git a/threads/.gitignore b/threads/.gitignore new file mode 100644 index 0000000..03beadd --- /dev/null +++ b/threads/.gitignore @@ -0,0 +1,4 @@ +/.vscode +/dist +/node_modules +/types diff --git a/threads/.swcrc b/threads/.swcrc new file mode 100644 index 0000000..6363ccf --- /dev/null +++ b/threads/.swcrc @@ -0,0 +1,18 @@ +{ + "$schema": "https://json.schemastore.org/swcrc", + "jsc": { + "parser": { + "syntax": "typescript", + "tsx": false, + "dynamicImport": false, + "decorators": false, + "dts": true + }, + "transform": {}, + "target": "esnext", + "loose": false, + "externalHelpers": false, + "keepClassNames": true + }, + "minify": true +} diff --git a/threads/README.md b/threads/README.md new file mode 100644 index 0000000..5dc89ae --- /dev/null +++ b/threads/README.md @@ -0,0 +1,24 @@ +# A pure javascript shim for WASI Preview 1 threads + +> [!WARNING] +> The code in this directory is less production ready than the main browser_wasi_shim code. + +This project is implement threads on browser_wasi_shim + +# Features +- [x] thread creation +- [x] Filesystem wrapper accessible by multiple workers +- [ ] thread pool + +# Building +```sh +$ npm install +$ npm run build +``` + +# Running the demo +```sh +$ git submodule update --init +$ cd examples && npm install && npm run dev +``` +And visit http://localhost diff --git a/threads/biome.json b/threads/biome.json new file mode 100644 index 0000000..33e0c8e --- /dev/null +++ b/threads/biome.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.9.1/schema.json", + "vcs": { + "enabled": false, + "clientKind": "git", + "useIgnoreFile": false + }, + "files": { + "ignoreUnknown": false, + "ignore": [] + }, + "formatter": { + "enabled": true, + "indentStyle": "space" + }, + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + } + } +} diff --git a/threads/bun.lockb b/threads/bun.lockb new file mode 100644 index 0000000..e0c24ec Binary files /dev/null and b/threads/bun.lockb differ diff --git a/threads/examples/bun.lockb b/threads/examples/bun.lockb new file mode 100644 index 0000000..005949e Binary files /dev/null and b/threads/examples/bun.lockb differ diff --git a/threads/examples/package.json b/threads/examples/package.json new file mode 100644 index 0000000..440b91e --- /dev/null +++ b/threads/examples/package.json @@ -0,0 +1,8 @@ +{ + "dependencies": { + "@oligami/shared-object": "^0.1.1", + "@xterm/xterm": "^5.5", + "xterm-addon-fit": "^0.8.0", + "@bjorn3/browser_wasi_shim": "^0.3.0" + } +} diff --git a/threads/examples/wasi_multi_threads/index.html b/threads/examples/wasi_multi_threads/index.html new file mode 100644 index 0000000..f111ef3 --- /dev/null +++ b/threads/examples/wasi_multi_threads/index.html @@ -0,0 +1,12 @@ + + + + + +

+ #### +

+ + + + diff --git a/threads/examples/wasi_multi_threads/index.ts b/threads/examples/wasi_multi_threads/index.ts new file mode 100644 index 0000000..f9f27af --- /dev/null +++ b/threads/examples/wasi_multi_threads/index.ts @@ -0,0 +1,27 @@ +import { WASIFarm, WASIFarmAnimal } from "../../src"; +import { + File, + OpenFile, + ConsoleStdout, + PreopenDirectory, +} from "@bjorn3/browser_wasi_shim"; + +const farm = new WASIFarm( + new OpenFile(new File([])), // stdin + ConsoleStdout.lineBuffered((msg) => console.log(`[WASI stdout] ${msg}`)), + ConsoleStdout.lineBuffered((msg) => console.warn(`[WASI stderr] ${msg}`)), + [], +); + +console.log(farm); + +const worker = new Worker("worker.ts", { type: "module" }); +// const worker = new Worker(new URL("./worker.js", import.meta.url).href, { type: "module" }); + +console.log(worker); + +worker.postMessage({ + wasi_ref: farm.get_ref(), +}); + +console.log("Sent WASI ref to worker"); diff --git a/threads/examples/wasi_multi_threads/main.rs b/threads/examples/wasi_multi_threads/main.rs new file mode 100644 index 0000000..86d14dd --- /dev/null +++ b/threads/examples/wasi_multi_threads/main.rs @@ -0,0 +1,13 @@ +fn main() { + println!("Hello, world!"); + + let _: std::thread::JoinHandle<()> = std::thread::spawn(|| { + for i in 1..1000 { + println!("hi number {} from the spawned thread!", i); + } + }); + + for i in 1..1000 { + println!("hi number {} from the main thread!", i); + } +} diff --git a/threads/examples/wasi_multi_threads/multi_thread_echo.wasm b/threads/examples/wasi_multi_threads/multi_thread_echo.wasm new file mode 100644 index 0000000..894878b Binary files /dev/null and b/threads/examples/wasi_multi_threads/multi_thread_echo.wasm differ diff --git a/threads/examples/wasi_multi_threads/thread_spawn.ts b/threads/examples/wasi_multi_threads/thread_spawn.ts new file mode 100644 index 0000000..3bb3a39 --- /dev/null +++ b/threads/examples/wasi_multi_threads/thread_spawn.ts @@ -0,0 +1,5 @@ +import { thread_spawn_on_worker } from "../../src"; + +self.onmessage = (event) => { + thread_spawn_on_worker(event.data); +}; diff --git a/threads/examples/wasi_multi_threads/worker.ts b/threads/examples/wasi_multi_threads/worker.ts new file mode 100644 index 0000000..96bb99b --- /dev/null +++ b/threads/examples/wasi_multi_threads/worker.ts @@ -0,0 +1,37 @@ +import { WASIFarmAnimal } from "../../src"; + +self.onmessage = async (e) => { + const { wasi_ref } = e.data; + + const wasm = await WebAssembly.compileStreaming( + fetch("./multi_thread_echo.wasm"), + ); + + const wasi = new WASIFarmAnimal( + wasi_ref, + [], // args + [], // env + { + can_thread_spawn: true, + thread_spawn_worker_url: new URL("./thread_spawn.ts", import.meta.url) + .href, + thread_spawn_wasm: wasm, + }, + ); + + await wasi.wait_worker_background_worker(); + + const inst = await WebAssembly.instantiate(wasm, { + env: { + memory: wasi.get_share_memory(), + }, + wasi: wasi.wasiThreadImport, + wasi_snapshot_preview1: wasi.wasiImport, + }); + + wasi.start( + inst as unknown as { + exports: { memory: WebAssembly.Memory; _start: () => unknown }; + }, + ); +}; diff --git a/threads/examples/wasi_multi_threads_channel/channel.wasm b/threads/examples/wasi_multi_threads_channel/channel.wasm new file mode 100644 index 0000000..369c3b5 Binary files /dev/null and b/threads/examples/wasi_multi_threads_channel/channel.wasm differ diff --git a/threads/examples/wasi_multi_threads_channel/index.html b/threads/examples/wasi_multi_threads_channel/index.html new file mode 100644 index 0000000..f111ef3 --- /dev/null +++ b/threads/examples/wasi_multi_threads_channel/index.html @@ -0,0 +1,12 @@ + + + + + +

+ #### +

+ + + + diff --git a/threads/examples/wasi_multi_threads_channel/index.ts b/threads/examples/wasi_multi_threads_channel/index.ts new file mode 100644 index 0000000..1eca130 --- /dev/null +++ b/threads/examples/wasi_multi_threads_channel/index.ts @@ -0,0 +1,15 @@ +import { OpenFile, File, ConsoleStdout } from "@bjorn3/browser_wasi_shim"; +import { WASIFarm } from "../../src"; + +const farm = new WASIFarm( + new OpenFile(new File([])), // stdin + ConsoleStdout.lineBuffered((msg) => console.log(`[WASI stdout] ${msg}`)), + ConsoleStdout.lineBuffered((msg) => console.warn(`[WASI stderr] ${msg}`)), + [], +); + +const worker = new Worker("./worker.ts", { type: "module" }); + +worker.postMessage({ + wasi_ref: farm.get_ref(), +}); diff --git a/threads/examples/wasi_multi_threads_channel/main.rs b/threads/examples/wasi_multi_threads_channel/main.rs new file mode 100644 index 0000000..216800c --- /dev/null +++ b/threads/examples/wasi_multi_threads_channel/main.rs @@ -0,0 +1,16 @@ +// https://doc.rust-lang.org/book/ch16-02-message-passing.html + +use std::sync::mpsc; +use std::thread; + +fn main() { + let (tx, rx) = mpsc::channel(); + + thread::spawn(move || { + let val = String::from("hi"); + tx.send(val).unwrap(); + }); + + let received = rx.recv().unwrap(); + println!("Got: {received}"); +} diff --git a/threads/examples/wasi_multi_threads_channel/thread_spawn.ts b/threads/examples/wasi_multi_threads_channel/thread_spawn.ts new file mode 100644 index 0000000..3bb3a39 --- /dev/null +++ b/threads/examples/wasi_multi_threads_channel/thread_spawn.ts @@ -0,0 +1,5 @@ +import { thread_spawn_on_worker } from "../../src"; + +self.onmessage = (event) => { + thread_spawn_on_worker(event.data); +}; diff --git a/threads/examples/wasi_multi_threads_channel/worker.ts b/threads/examples/wasi_multi_threads_channel/worker.ts new file mode 100644 index 0000000..0b926e0 --- /dev/null +++ b/threads/examples/wasi_multi_threads_channel/worker.ts @@ -0,0 +1,36 @@ +import { WASIFarmAnimal } from "../../src"; + +self.onmessage = async (e) => { + const { wasi_ref } = e.data; + + const wasm = await WebAssembly.compileStreaming(fetch("./channel.wasm")); + + const wasi = new WASIFarmAnimal( + wasi_ref, + [], // args + [], // env + { + can_thread_spawn: true, + thread_spawn_worker_url: new URL("./thread_spawn.ts", import.meta.url) + .href, + // thread_spawn_worker_url: "./thread_spawn.ts", + thread_spawn_wasm: wasm, + }, + ); + + await wasi.wait_worker_background_worker(); + + const inst = await WebAssembly.instantiate(wasm, { + env: { + memory: wasi.get_share_memory(), + }, + wasi: wasi.wasiThreadImport, + wasi_snapshot_preview1: wasi.wasiImport, + }); + + wasi.start( + inst as unknown as { + exports: { memory: WebAssembly.Memory; _start: () => unknown }; + }, + ); +}; diff --git a/threads/examples/wasi_multi_threads_rustc/clang.ts b/threads/examples/wasi_multi_threads_rustc/clang.ts new file mode 100644 index 0000000..6b43ac2 --- /dev/null +++ b/threads/examples/wasi_multi_threads_rustc/clang.ts @@ -0,0 +1,76 @@ +import { strace } from "@bjorn3/browser_wasi_shim"; +import { WASIFarmAnimal } from "../../src"; +import { SharedObject } from "@oligami/shared-object"; + +let wasi: WASIFarmAnimal; +let inst: { + exports: { memory: WebAssembly.Memory; _start: () => unknown }; +}; +let wasm: WebAssembly.Module; + +let shared_clang: SharedObject; +let shared_tools: SharedObject; +let shared_wasm_ld: SharedObject; + +globalThis.onmessage = async (e) => { + const { wasi_refs } = e.data; + + if (wasi_refs) { + wasm = await WebAssembly.compileStreaming( + fetch("./rust_wasm/llvm-tools/llvm-opt.wasm"), + ); + + wasi = new WASIFarmAnimal( + wasi_refs, + ["llvm"], // args + [], // env + ); + + // Memory is rewritten at this time. + // inst = await WebAssembly.instantiate(wasm, { + // wasi_snapshot_preview1: wasi.wasiImport, + // }); + inst = (await WebAssembly.instantiate(wasm, { + wasi_snapshot_preview1: strace(wasi.wasiImport, []), + })) as unknown as { + exports: { memory: WebAssembly.Memory; _start: () => unknown }; + }; + + console.log("wasi.start, inst", inst); + + const memory_reset = inst.exports.memory.buffer; + const memory_reset_view = new Uint8Array(memory_reset).slice(); + + shared_clang = new SharedObject((...args) => { + console.log("clang args", args); + // If I don't reset memory, I get some kind of error. + wasi.args = ["llvm", "clang", ...args]; + const memory_view = new Uint8Array(inst.exports.memory.buffer); + memory_view.set(memory_reset_view); + wasi.start(inst); + console.log("clang wasi.start done"); + }, "clang"); + + shared_tools = new SharedObject((...args) => { + console.log("tools args", args); + // If I don't reset memory, I get some kind of error. + wasi.args = ["llvm-tools", ...args]; + const memory_view = new Uint8Array(inst.exports.memory.buffer); + memory_view.set(memory_reset_view); + wasi.start(inst); + console.log("tools wasi.start done"); + }, "llvm-tools"); + + shared_wasm_ld = new SharedObject((...args) => { + console.log("wasm-ld args", args); + // If I don't reset memory, I get some kind of error. + wasi.args = ["llvm-tools", "wasm-ld", ...args]; + const memory_view = new Uint8Array(inst.exports.memory.buffer); + memory_view.set(memory_reset_view); + wasi.start(inst); + console.log("wasm-ld wasi.start done"); + }, "wasm-ld"); + + postMessage({ ready: true }); + } +}; diff --git a/threads/examples/wasi_multi_threads_rustc/depend_clang_files.ts b/threads/examples/wasi_multi_threads_rustc/depend_clang_files.ts new file mode 100644 index 0000000..5f3a3a6 --- /dev/null +++ b/threads/examples/wasi_multi_threads_rustc/depend_clang_files.ts @@ -0,0 +1,86 @@ +import { WASIFarm } from "../../src"; +import { + Directory, + File, + type Inode, + PreopenDirectory, +} from "@bjorn3/browser_wasi_shim"; + +async function load_external_file(path) { + return new File(await (await (await fetch(path)).blob()).arrayBuffer()); +} + +const wasi_libs = new Map(); +for (const file of [ + "crt1-command.o", + "crt1-reactor.o", + "crt1.o", + "libc-printscan-long-double.a", + "libc-printscan-no-floating-point.a", + "libc.a", + "libc.imports", + "libc++.a", + "libc++.modules.json", + "libc++abi.a", + "libc++experimental.a", + "libcrypt.a", + "libdl.a", + "libm.a", + "libpthread.a", + "libresolv.a", + "librt.a", + "libsetjmp.a", + "libutil.a", + "libwasi-emulated-getpid.a", + "libwasi-emulated-mman.a", + "libwasi-emulated-process-clocks.a", + "libwasi-emulated-signal.a", + "libxnet.a", +]) { + wasi_libs.set( + file, + await load_external_file( + `./rust_wasm/llvm-tools/dist/lib/wasm32-wasip1/${file}`, + ), + ); +} + +const toMap = (arr: Array<[string, Inode]>) => new Map(arr); + +const farm = new WASIFarm( + undefined, + undefined, + undefined, + [ + new PreopenDirectory( + "/sysroot-clang", + toMap([ + [ + "lib", + new Directory([ + [ + "wasm32-unknown-wasip1", + new Directory([ + [ + "libclang_rt.builtins.a", + await load_external_file( + "./rust_wasm/llvm-tools/dist/lib/wasm32-unknown-wasip1/libclang_rt.builtins.a", + ), + ], + ]), + ], + ["wasm32-wasip1", new Directory(wasi_libs)], + ]), + ], + ]), + ), + ], + { + // allocator_size: 1024 * 1024 * 1024, + // debug: true, + }, +); + +const ret = await farm.get_ref(); + +postMessage({ wasi_ref: ret }); diff --git a/threads/examples/wasi_multi_threads_rustc/depend_rustc_files.ts b/threads/examples/wasi_multi_threads_rustc/depend_rustc_files.ts new file mode 100644 index 0000000..4dc8153 --- /dev/null +++ b/threads/examples/wasi_multi_threads_rustc/depend_rustc_files.ts @@ -0,0 +1,160 @@ +import { + Directory, + PreopenDirectory, + File, + type Inode, +} from "@bjorn3/browser_wasi_shim"; +import { WASIFarm } from "../../src"; + +async function load_external_file(path) { + return new File(await (await (await fetch(path)).blob()).arrayBuffer()); +} + +const linux_libs_promise = (async () => { + const dir = new Map(); + for (const file of [ + "libaddr2line-b8754aeb03c02354.rlib", + "libadler-05c3545f6cd12159.rlib", + "liballoc-0dab879bc41cd6bd.rlib", + "libcfg_if-c7fd2cef50341546.rlib", + "libcompiler_builtins-a99947d020d809d6.rlib", + "libcore-4b8e8a815d049db3.rlib", + "libgetopts-bbb75529e85d129d.rlib", + "libgimli-598847d27d7a3cbf.rlib", + "libhashbrown-d2ff91fdf93cacb2.rlib", + "liblibc-dc63949c664c3fce.rlib", + "libmemchr-2d3a423be1a6cb96.rlib", + "libminiz_oxide-b109506a0ccc4c6a.rlib", + "libobject-7b48def7544c748b.rlib", + "libpanic_abort-c93441899b93b849.rlib", + "libpanic_unwind-11d9ba05b60bf694.rlib", + "libproc_macro-1a7f7840bb9983dc.rlib", + "librustc_demangle-59342a335246393d.rlib", + "librustc_std_workspace_alloc-552b185085090ff6.rlib", + "librustc_std_workspace_core-5d8a121daa7eeaa9.rlib", + "librustc_std_workspace_std-97f43841ce452f7d.rlib", + "libstd-bdedb7706a556da2.rlib", + "libstd-bdedb7706a556da2.so", + "libstd_detect-cca21eebc4281add.rlib", + "libsysroot-f654e185be3ffebd.rlib", + "libtest-f06fa3fbc201c558.rlib", + "libunicode_width-19a0dcd589fa0877.rlib", + "libunwind-747b693f90af9445.rlib", + ]) { + dir.set( + file, + await load_external_file( + `./rust_wasm/rustc_llvm/dist/lib/rustlib/x86_64-unknown-linux-gnu/lib/${file}`, + ), + ); + } + return dir; +})(); + +const threads_libs_promise = (async () => { + const dir = new Map(); + for (const file of [ + "libaddr2line-a47658bebc67c3a1.rlib", + "libadler-38ddbcf07afd45fc.rlib", + "liballoc-1fc4f6ca1d836e4c.rlib", + "libcfg_if-fd15f5d506df7899.rlib", + "libcompiler_builtins-3dc6223f56552b05.rlib", + "libcore-0ec7cb16e8553802.rlib", + "libgetopts-6248a91c42a854a0.rlib", + "libgimli-4425159eeeeb18dd.rlib", + "libhashbrown-243f98c4e4e641ea.rlib", + "liblibc-9149392e3841960d.rlib", + "libmemchr-9ac950afd37fa4c7.rlib", + "libminiz_oxide-91aaa0ee7402d39e.rlib", + "libobject-361b96ef5df8a7f9.rlib", + "libpanic_abort-f91052098501e46b.rlib", + "libpanic_unwind-fc376dcf47815f10.rlib", + "libproc_macro-9cab37e4d11f0e52.rlib", + "librustc_demangle-1af142f261139812.rlib", + "librustc_std_workspace_alloc-f0d62212c413dd0e.rlib", + "librustc_std_workspace_core-ea396731d16229a8.rlib", + "librustc_std_workspace_std-7434133be68a4a89.rlib", + "libstd_detect-083332b3c8180bc9.rlib", + "libstd-5ddf10249e9580fe.rlib", + "libsysroot-8b3608099dad3b42.rlib", + "libtest-8ebd431ae5608538.rlib", + "libunicode_width-7e2396fcd7049a8b.rlib", + "libunwind-e7408208cf4a3c79.rlib", + "libwasi-f0b9e157c50fe586.rlib", + ]) { + dir.set( + file, + await load_external_file( + `./rust_wasm/rustc_llvm/dist/lib/rustlib/wasm32-wasip1-threads/lib/${file}`, + ), + ); + } + return dir; +})(); + +const threads_self_contained_promise = (async () => { + const dir = new Map(); + for (const file of ["crt1-command.o", "crt1-reactor.o", "libc.a"]) { + dir.set( + file, + await load_external_file( + `./rust_wasm/rustc_llvm/dist/lib/rustlib/wasm32-wasip1-threads/lib/self-contained/${file}`, + ), + ); + } + return dir; +})(); + +const [linux_libs, threads_libs, threads_self_contained, components] = + await Promise.all([ + linux_libs_promise, + threads_libs_promise, + threads_self_contained_promise, + await load_external_file( + "./rust_wasm/rustc_llvm/dist/lib/rustlib/components", + ), + ]); + +threads_libs.set("self-contained", new Directory(threads_self_contained)); + +const toMap = (arr: Array<[string, Inode]>) => new Map(arr); + +const farm = new WASIFarm( + undefined, + undefined, + undefined, + [ + new PreopenDirectory( + "/sysroot", + toMap([ + [ + "lib", + new Directory([ + [ + "rustlib", + new Directory([ + ["components", components], + [ + "wasm32-wasip1-threads", + new Directory([["lib", new Directory(threads_libs)]]), + ], + [ + "x86_64-unknown-linux-gnu", + new Directory([["lib", new Directory(linux_libs)]]), + ], + ]), + ], + ]), + ], + ]), + ), + ], + { + allocator_size: 1024 * 1024 * 1024, + // debug: true, + }, +); + +const ret = await farm.get_ref(); + +postMessage({ wasi_ref: ret }); diff --git a/threads/examples/wasi_multi_threads_rustc/depend_rustc_with_lld.ts b/threads/examples/wasi_multi_threads_rustc/depend_rustc_with_lld.ts new file mode 100644 index 0000000..9dac3a1 --- /dev/null +++ b/threads/examples/wasi_multi_threads_rustc/depend_rustc_with_lld.ts @@ -0,0 +1,161 @@ +import { WASIFarm } from "../../src"; + +import { + File, + Directory, + PreopenDirectory, + type Inode, +} from "@bjorn3/browser_wasi_shim"; + +async function load_external_file(path) { + return new File(await (await (await fetch(path)).blob()).arrayBuffer()); +} + +const linux_libs_promise = (async () => { + const dir = new Map(); + for (const file of [ + "libaddr2line-d2445d88f0df8258.rlib", + "libadler-6095b59b8443d84e.rlib", + "liballoc-19b196c8c1c1b105.rlib", + "libcfg_if-d04d665f43a7a5c9.rlib", + "libcompiler_builtins-f3e5bc67a3085e50.rlib", + "libcore-24518d8502db248c.rlib", + "libgetopts-4bb89b05e2b4cc6e.rlib", + "libgimli-85e2d283537da979.rlib", + "libhashbrown-1436b1713a4e6650.rlib", + "liblibc-a012fa771333437a.rlib", + "libmemchr-b50e0c33c9e9768d.rlib", + "libminiz_oxide-937a56bed56199f4.rlib", + "libobject-4e591f1863579f49.rlib", + "libpanic_abort-90cfe20cb97a5e4c.rlib", + "libpanic_unwind-9ad8c03a583f4dc7.rlib", + "libproc_macro-32b9efef039a24fe.rlib", + "librustc_demangle-ca292a161705fdb4.rlib", + "librustc_std_workspace_alloc-bba69521e853a996.rlib", + "librustc_std_workspace_core-3da34b7f5e59869a.rlib", + "librustc_std_workspace_std-c35ef78edc033606.rlib", + "libstd_detect-776a1ebea822ca12.rlib", + "libstd-6924b036e1bec7ce.rlib", + "libstd-6924b036e1bec7ce.so", + "libsysroot-0b7644c6027c414e.rlib", + "libtest-3d5766d8038a0e74.rlib", + "libunicode_width-dff2b02e7e936b79.rlib", + "libunwind-c99628283276f21f.rlib", + ]) { + dir.set( + file, + await load_external_file( + `./rust_wasm/rustc_llvm_with_lld/dist/lib/rustlib/x86_64-unknown-linux-gnu/lib/${file}`, + ), + ); + } + return dir; +})(); + +const threads_libs_promise = (async () => { + const dir = new Map(); + for (const file of [ + "libaddr2line-02cc3b87379ea949.rlib", + "libgimli-a80bd3f5fe54def6.rlib", + "libpanic_unwind-05c50d12758b6d01.rlib", + "libstd_detect-a196185ed7f17cfc.rlib", + "libadler-a6b90b86640ec179.rlib", + "libhashbrown-35a68b834152af94.rlib", + "libproc_macro-2aa22b2ed111e644.rlib", + "libsysroot-856cc79af2fd1ee2.rlib", + "liballoc-be9b2b68a2d6adbd.rlib", + "liblibc-374b1b6cc5790f18.rlib", + "librustc_demangle-710d955f336192eb.rlib", + "libtest-4fcaddbdfa4f37f9.rlib", + "libcfg_if-75a956684c8aceef.rlib", + "libmemchr-b8580e949eadd97a.rlib", + "librustc_std_workspace_alloc-9c960f87e9d5a453.rlib", + "libunicode_width-be3becba43cd1b78.rlib", + "libcompiler_builtins-3a0795e4489d8e8b.rlib", + "libminiz_oxide-0bc4b046969a6755.rlib", + "librustc_std_workspace_core-4e237761d66d6cde.rlib", + "libunwind-2e570680d1c4cd0a.rlib", + "libcore-3e70038323b3a06e.rlib", + "libobject-3d83ea5d7ed5636f.rlib", + "librustc_std_workspace_std-5aa56e0a1970dc72.rlib", + "libwasi-f7d0229d2fe97cfd.rlib", + "libgetopts-b3d0219ad62c74a7.rlib", + "libpanic_abort-431cc2501a123c59.rlib", + "libstd-c7f97b33ddfcbfbf.rlib", + ]) { + dir.set( + file, + await load_external_file( + `./rust_wasm/rustc_llvm_with_lld/dist/lib/rustlib/wasm32-wasip1/lib/${file}`, + ), + ); + } + return dir; +})(); + +const threads_self_contained_promise = (async () => { + const dir = new Map(); + for (const file of ["crt1-command.o", "crt1-reactor.o", "libc.a"]) { + dir.set( + file, + await load_external_file( + `./rust_wasm/rustc_llvm_with_lld/dist/lib/rustlib/wasm32-wasip1/lib/self-contained/${file}`, + ), + ); + } + return dir; +})(); + +const [linux_libs, threads_libs, threads_self_contained, components] = + await Promise.all([ + linux_libs_promise, + threads_libs_promise, + threads_self_contained_promise, + await load_external_file( + "./rust_wasm/rustc_llvm_with_lld/dist/lib/rustlib/components", + ), + ]); + +threads_libs.set("self-contained", new Directory(threads_self_contained)); + +const toMap = (arr: Array<[string, Inode]>) => new Map(arr); + +const farm = new WASIFarm( + undefined, + undefined, + undefined, + [ + new PreopenDirectory( + "/sysroot-with-lld", + toMap([ + [ + "lib", + new Directory([ + [ + "rustlib", + new Directory([ + ["components", components], + [ + "wasm32-wasip1", + new Directory([["lib", new Directory(threads_libs)]]), + ], + [ + "x86_64-unknown-linux-gnu", + new Directory([["lib", new Directory(linux_libs)]]), + ], + ]), + ], + ]), + ], + ]), + ), + ], + { + allocator_size: 1024 * 1024 * 1024, + // debug: true, + }, +); + +const ret = await farm.get_ref(); + +postMessage({ wasi_ref: ret }); diff --git a/threads/examples/wasi_multi_threads_rustc/index.html b/threads/examples/wasi_multi_threads_rustc/index.html new file mode 100644 index 0000000..0235f6b --- /dev/null +++ b/threads/examples/wasi_multi_threads_rustc/index.html @@ -0,0 +1,13 @@ + + + + + +
+ + + + + + diff --git a/threads/examples/wasi_multi_threads_rustc/index.ts b/threads/examples/wasi_multi_threads_rustc/index.ts new file mode 100644 index 0000000..20cd1db --- /dev/null +++ b/threads/examples/wasi_multi_threads_rustc/index.ts @@ -0,0 +1,67 @@ +import { Terminal } from "@xterm/xterm"; +import { WASIFarm } from "../../src"; +import { FitAddon } from "xterm-addon-fit"; +import { SharedObject } from "@oligami/shared-object"; +import { Fd } from "@bjorn3/browser_wasi_shim"; + +import "@xterm/xterm/css/xterm.css"; + +const term = new Terminal({ + convertEol: true, +}); +const terminalElement = document.getElementById("terminal"); + +if (!terminalElement) { + throw new Error("No terminal element found"); +} + +term.open(terminalElement); + +const fitAddon = new FitAddon(); +term.loadAddon(fitAddon); +fitAddon.fit(); + +// term.onData(data => { +// term.write(data); +// }); + +const shared = new SharedObject( + { + writeln(data) { + term.writeln(data); + }, + writeUtf8(data) { + term.write(new TextDecoder().decode(data)); + }, + }, + "term", +); + +term.writeln("Initializing WASI..."); + +class XtermStdio extends Fd { + term: Terminal; + + constructor(term: Terminal) { + super(); + this.term = term; + } + fd_write(data: Uint8Array) /*: {ret: number, nwritten: number}*/ { + this.term.write(new TextDecoder().decode(data)); + return { ret: 0, nwritten: data.byteLength }; + } +} + +const farm = new WASIFarm( + new XtermStdio(term), + new XtermStdio(term), + new XtermStdio(term), + [], + // { debug: true }, +); + +const worker = new Worker("./worker.ts", { type: "module" }); + +worker.postMessage({ + wasi_ref: farm.get_ref(), +}); diff --git a/threads/examples/wasi_multi_threads_rustc/rust_wasm b/threads/examples/wasi_multi_threads_rustc/rust_wasm new file mode 160000 index 0000000..76de7f7 --- /dev/null +++ b/threads/examples/wasi_multi_threads_rustc/rust_wasm @@ -0,0 +1 @@ +Subproject commit 76de7f7133b0da1e89770ec30fd05026fdbc7e53 diff --git a/threads/examples/wasi_multi_threads_rustc/rustc.ts b/threads/examples/wasi_multi_threads_rustc/rustc.ts new file mode 100644 index 0000000..b5683d6 --- /dev/null +++ b/threads/examples/wasi_multi_threads_rustc/rustc.ts @@ -0,0 +1,49 @@ +import { SharedObject } from "@oligami/shared-object"; +import { WASIFarmAnimal } from "../../src"; + +const { promise, resolve } = Promise.withResolvers(); +import("@oligami/shared-object").then(resolve); + +let wasi: WASIFarmAnimal; +let wasm: WebAssembly.Module; +let shared: SharedObject; + +globalThis.onmessage = async (e) => { + const { wasi_refs } = e.data; + + if (wasi_refs) { + wasm = await WebAssembly.compileStreaming( + fetch("./rust_wasm/rustc_llvm/rustc_opt.wasm"), + ); + + wasi = new WASIFarmAnimal( + wasi_refs, + [], // args + ["RUST_MIN_STACK=16777216"], // env + { + // debug: true, + can_thread_spawn: true, + thread_spawn_worker_url: new URL("./thread_spawn.ts", import.meta.url) + .href, + // thread_spawn_worker_url: "./thread_spawn.ts", + thread_spawn_wasm: wasm, + }, + ); + + await wasi.wait_worker_background_worker(); + + wasi.get_share_memory().grow(200); + + console.log("Waiting for worker background worker..."); + + await promise; + + shared = new SharedObject((...args) => { + wasi.args = ["rustc", ...args]; + wasi.block_start_on_thread(); + console.log("wasi.start done"); + }, "rustc"); + + postMessage({ ready: true }); + } +}; diff --git a/threads/examples/wasi_multi_threads_rustc/rustc_with_lld.ts b/threads/examples/wasi_multi_threads_rustc/rustc_with_lld.ts new file mode 100644 index 0000000..defb5ae --- /dev/null +++ b/threads/examples/wasi_multi_threads_rustc/rustc_with_lld.ts @@ -0,0 +1,44 @@ +import { SharedObject } from "@oligami/shared-object"; +import { WASIFarmAnimal } from "../../src"; + +let wasi: WASIFarmAnimal; +let wasm: WebAssembly.Module; +let shared: SharedObject; + +globalThis.onmessage = async (e) => { + const { wasi_refs } = e.data; + + if (wasi_refs) { + wasm = await WebAssembly.compileStreaming( + fetch("./rust_wasm/rustc_llvm_with_lld/rustc_opt.wasm"), + ); + + wasi = new WASIFarmAnimal( + wasi_refs, + [], // args + ["RUST_MIN_STACK=16777216"], // env + { + // debug: true, + can_thread_spawn: true, + thread_spawn_worker_url: new URL("./thread_spawn.ts", import.meta.url) + .href, + // thread_spawn_worker_url: "./thread_spawn.ts", + thread_spawn_wasm: wasm, + }, + ); + + await wasi.wait_worker_background_worker(); + + wasi.get_share_memory().grow(200); + + console.log("Waiting for worker background worker..."); + + shared = new SharedObject((...args) => { + wasi.args = ["rustc_with_lld", ...args]; + wasi.block_start_on_thread(); + console.log("wasi.start done"); + }, "rustc_with_lld"); + + postMessage({ ready: true }); + } +}; diff --git a/threads/examples/wasi_multi_threads_rustc/save_stdout.ts b/threads/examples/wasi_multi_threads_rustc/save_stdout.ts new file mode 100644 index 0000000..4aa4819 --- /dev/null +++ b/threads/examples/wasi_multi_threads_rustc/save_stdout.ts @@ -0,0 +1,63 @@ +import { SharedObject, SharedObjectRef } from "@oligami/shared-object"; +import { WASIFarm } from "../../src"; +import { Fd } from "@bjorn3/browser_wasi_shim"; +import { resolve } from "node:path"; + +const term = new SharedObjectRef("term").proxy<{ + writeln: (data) => Promise; + writeUtf8: (data) => Promise; +}>(); + +class Stdout extends Fd { + buffer = new Uint8Array(0); + + fd_write(data: Uint8Array): { ret: number; nwritten: number } { + const { promise, resolve } = Promise.withResolvers(); + + (async () => { + const new_buffer = new Uint8Array(this.buffer.length + data.length); + new_buffer.set(this.buffer); + new_buffer.set(data, this.buffer.length); + this.buffer = new_buffer; + await term.writeUtf8(data); + + resolve({ ret: 0, nwritten: data.byteLength }); + })(); + + return promise as unknown as { ret: number; nwritten: number }; + } +} + +const std_out = new Stdout(); + +const std_err = new Stdout(); + +const shared_std_out = new SharedObject( + { + get() { + return new TextDecoder().decode(std_out.buffer); + }, + reset() { + std_out.buffer = new Uint8Array(0); + }, + }, + "std_out_keep", +); + +const shared_std_err = new SharedObject( + { + get() { + return new TextDecoder().decode(std_err.buffer); + }, + reset() { + std_err.buffer = new Uint8Array(0); + }, + }, + "std_err_keep", +); + +const farm = new WASIFarm(undefined, std_out, std_err); + +const ret = await farm.get_ref(); + +postMessage({ wasi_ref: ret }); diff --git a/threads/examples/wasi_multi_threads_rustc/thread_spawn.ts b/threads/examples/wasi_multi_threads_rustc/thread_spawn.ts new file mode 100644 index 0000000..7fc9aa2 --- /dev/null +++ b/threads/examples/wasi_multi_threads_rustc/thread_spawn.ts @@ -0,0 +1,5 @@ +import { thread_spawn_on_worker } from "../../src"; + +self.onmessage = async (event) => { + await thread_spawn_on_worker(event.data); +}; diff --git a/threads/examples/wasi_multi_threads_rustc/tmp_dir.ts b/threads/examples/wasi_multi_threads_rustc/tmp_dir.ts new file mode 100644 index 0000000..0ca6152 --- /dev/null +++ b/threads/examples/wasi_multi_threads_rustc/tmp_dir.ts @@ -0,0 +1,86 @@ +import { + type Inode, + PreopenDirectory, + File, + Directory, +} from "@bjorn3/browser_wasi_shim"; +import { WASIFarm } from "../../src"; +import { SharedObject } from "@oligami/shared-object"; + +const toMap = (arr: Array<[string, Inode]>) => new Map(arr); + +const root_dir = new PreopenDirectory( + "/", + toMap([ + [ + "hello.rs", + new File( + new TextEncoder().encode(`fn main() { println!("Hello World!"); }`), + ), + ], + ["sysroot", new Directory([])], + ["sysroot-with-lld", new Directory([])], + ["tmp", new Directory([])], + ]), +); + +const farm = new WASIFarm( + undefined, + undefined, + undefined, + [ + // new PreopenDirectory(".", [ + // ["tmp-tmp", new File(new TextEncoder("utf-8").encode("Hello World!"))], + // ["tmp-dir", new Directory([])], + // ]), + // new PreopenDirectory("tmp-dir", [ + // [ + // "tmp-dir_inner", + // new Directory([ + // [ + // "tmp-dir_inner-file", + // new File(new TextEncoder("utf-8").encode("Hello World!!!!!")), + // ], + // ]), + // ], + // ]), + new PreopenDirectory("/tmp", toMap([])), + root_dir, + new PreopenDirectory( + "~", + toMap([ + [ + "####.rs", + new File( + new TextEncoder().encode(`fn main() { println!("Hello World!"); }`), + ), + ], + ["sysroot", new Directory([])], + ]), + ), + ], + // { debug: true }, +); + +const ret = await farm.get_ref(); + +const shared = new SharedObject( + { + get_file(path_str) { + console.log(root_dir); + const path = { + parts: [path_str], + is_dir: false, + }; + // biome-ignore lint/suspicious/noExplicitAny: + const { ret, entry } = root_dir.dir.get_entry_for_path(path as any); + if (ret !== 0 || entry === null) { + throw new Error(`get_file: ${path_str} failed`); + } + return (entry as File).data; + }, + }, + "root_dir", +); + +postMessage({ wasi_ref: ret }); diff --git a/threads/examples/wasi_multi_threads_rustc/tre_opt.wasm b/threads/examples/wasi_multi_threads_rustc/tre_opt.wasm new file mode 100644 index 0000000..67f06df Binary files /dev/null and b/threads/examples/wasi_multi_threads_rustc/tre_opt.wasm differ diff --git a/threads/examples/wasi_multi_threads_rustc/tree.ts b/threads/examples/wasi_multi_threads_rustc/tree.ts new file mode 100644 index 0000000..c748799 --- /dev/null +++ b/threads/examples/wasi_multi_threads_rustc/tree.ts @@ -0,0 +1,42 @@ +import { SharedObject } from "@oligami/shared-object"; +import { WASIFarmAnimal } from "../../src"; + +let inst: { exports: { memory: WebAssembly.Memory; _start: () => unknown } }; +let wasi: WASIFarmAnimal; +let wasm: WebAssembly.Module; + +let shared: SharedObject; + +globalThis.onmessage = async (e) => { + const { wasi_refs } = e.data; + + if (wasi_refs) { + wasm = await WebAssembly.compileStreaming(fetch("./tre_opt.wasm")); + + wasi = new WASIFarmAnimal( + wasi_refs, + ["tre"], // args + [], // env + ); + + // Memory is rewritten at this time. + inst = (await WebAssembly.instantiate(wasm, { + wasi_snapshot_preview1: wasi.wasiImport, + })) as unknown as { + exports: { memory: WebAssembly.Memory; _start: () => unknown }; + }; + + const memory_reset = inst.exports.memory.buffer; + const memory_reset_view = new Uint8Array(memory_reset).slice(); + + shared = new SharedObject((...args) => { + // If I don't reset memory, I get some kind of error. + wasi.args = ["tre", ...args]; + const memory_view = new Uint8Array(inst.exports.memory.buffer); + memory_view.set(memory_reset_view); + wasi.start(inst); + }, "tree"); + + postMessage({ ready: true }); + } +}; diff --git a/threads/examples/wasi_multi_threads_rustc/worker.ts b/threads/examples/wasi_multi_threads_rustc/worker.ts new file mode 100644 index 0000000..3777a60 --- /dev/null +++ b/threads/examples/wasi_multi_threads_rustc/worker.ts @@ -0,0 +1,370 @@ +import { SharedObjectRef } from "@oligami/shared-object"; +import { WASIFarmAnimal, type WASIFarmRef } from "../../src"; + +let tree: (...args: string[]) => void; +let term: { + writeln: (data) => Promise; + writeUtf8: (data) => Promise; +}; +let rustc: (...args: string[]) => void; +let rustc_with_lld: (...args: string[]) => void; +let clang: (...args: string[]) => void; +let llvm_tools: (...args: string[]) => void; +let wasm_ld: (...args: string[]) => void; +let std_out_keep: { + get: () => Promise; + reset: () => void; +}; +let std_err_keep: { + get: () => Promise; + reset: () => void; +}; +let root_dir: { + get_file: (path_str: string) => Promise; +}; + +const blueText = "\x1b[34m"; +const resetText = "\x1b[0m"; + +self.onmessage = async (e) => { + const { wasi_ref } = e.data; + + const { + promise: depend_rustc_files_promise, + resolve: depend_rustc_files_resolve, + } = Promise.withResolvers<{ wasi_ref: WASIFarmRef }>(); + const { + promise: depend_rustc_with_lld_promise, + resolve: depend_rustc_with_lld_resolve, + } = Promise.withResolvers<{ wasi_ref: WASIFarmRef }>(); + const { + promise: depend_clang_files_promise, + resolve: depend_clang_files_resolve, + } = Promise.withResolvers<{ wasi_ref: WASIFarmRef }>(); + const { promise: tmp_dir_promise, resolve: tmp_dir_resolve } = + Promise.withResolvers<{ wasi_ref: WASIFarmRef }>(); + const { promise: save_stdout_promise, resolve: save_stdout_resolve } = + Promise.withResolvers<{ wasi_ref: WASIFarmRef }>(); + + const depend_rustc_files_manage_worker = new Worker("depend_rustc_files.ts", { + type: "module", + }); + depend_rustc_files_manage_worker.onmessage = (e) => { + depend_rustc_files_resolve(e.data); + }; + + const depend_rustc_with_lld_manage_worker = new Worker( + "depend_rustc_with_lld.ts", + { + type: "module", + }, + ); + depend_rustc_with_lld_manage_worker.onmessage = (e) => { + depend_rustc_with_lld_resolve(e.data); + }; + + const depend_clang_files_manage_worker = new Worker("depend_clang_files.ts", { + type: "module", + }); + depend_clang_files_manage_worker.onmessage = (e) => { + depend_clang_files_resolve(e.data); + }; + + const tmp_dir_manage_worker = new Worker("tmp_dir.ts", { + type: "module", + }); + tmp_dir_manage_worker.onmessage = (e) => { + tmp_dir_resolve(e.data); + }; + + const save_stdout_manage_worker = new Worker("save_stdout.ts", { + type: "module", + }); + save_stdout_manage_worker.onmessage = (e) => { + save_stdout_resolve(e.data); + }; + + const [ + depend_rustc_files, + depend_rustc_with_lld, + depend_clang_files, + tmp_dir, + save_stdout, + ] = await Promise.all([ + depend_rustc_files_promise, + depend_rustc_with_lld_promise, + depend_clang_files_promise, + tmp_dir_promise, + save_stdout_promise, + ]); + const { wasi_ref: wasi_ref_depend_rustc_files } = depend_rustc_files; + const { wasi_ref: wasi_ref_depend_rustc_with_lld_files } = + depend_rustc_with_lld; + const { wasi_ref: wasi_ref_depend_clang_files } = depend_clang_files; + const { wasi_ref: wasi_ref_tmp_dir } = tmp_dir; + const { wasi_ref: wasi_ref_save_stdout } = save_stdout; + + const wasi_refs = [ + wasi_ref_depend_rustc_files, + wasi_ref_depend_rustc_with_lld_files, + wasi_ref_depend_clang_files, + wasi_ref_tmp_dir, + wasi_ref, + ]; + + const { promise: tree_promise, resolve: tree_resolve } = + Promise.withResolvers(); + const { promise: rustc_promise, resolve: rustc_resolve } = + Promise.withResolvers(); + const { promise: rustc_with_lld_promise, resolve: rustc_with_lld_resolve } = + Promise.withResolvers(); + const { promise: clang_promise, resolve: clang_resolve } = + Promise.withResolvers(); + + const tree_worker = new Worker("tree.ts", { + type: "module", + }); + tree_worker.onmessage = (e) => { + console.log("tree onmessage"); + tree_resolve(e.data); + }; + + const rustc_worker = new Worker("rustc.ts", { + type: "module", + }); + rustc_worker.onmessage = (e) => { + console.log("rustc onmessage"); + rustc_resolve(e.data); + }; + + const rustc_with_lld_worker = new Worker("rustc_with_lld.ts", { + type: "module", + }); + rustc_with_lld_worker.onmessage = (e) => { + console.log("rustc_with_lld onmessage"); + rustc_with_lld_resolve(e.data); + }; + + const clang_worker = new Worker("clang.ts", { + type: "module", + }); + clang_worker.onmessage = (e) => { + console.log("clang onmessage"); + clang_resolve(e.data); + }; + + tree_worker.postMessage({ + wasi_refs, + }); + rustc_worker.postMessage({ + wasi_refs: [wasi_ref_save_stdout, ...wasi_refs], + }); + rustc_with_lld_worker.postMessage({ + wasi_refs, + }); + clang_worker.postMessage({ + wasi_refs, + }); + + console.log("Waiting for tree and rustc to finish..."); + + await Promise.all([ + tree_promise, + rustc_promise, + rustc_with_lld_promise, + clang_promise, + ]); + + console.log("Sending run message..."); + + tree = new SharedObjectRef("tree").proxy<(...args: string[]) => void>(); + + term = new SharedObjectRef("term").proxy<{ + writeln: (data) => Promise; + writeUtf8: (data) => Promise; + }>(); + + rustc = new SharedObjectRef("rustc").proxy<(...args: string[]) => void>(); + + rustc_with_lld = new SharedObjectRef("rustc_with_lld").proxy< + (...args: string[]) => void + >(); + + clang = new SharedObjectRef("clang").proxy<(...args: string[]) => void>(); + + llvm_tools = new SharedObjectRef("llvm-tools").proxy< + (...args: string[]) => void + >(); + + wasm_ld = new SharedObjectRef("wasm-ld").proxy<(...args: string[]) => void>(); + + std_out_keep = new SharedObjectRef("std_out_keep").proxy<{ + get: () => Promise; + reset: () => void; + }>(); + + std_err_keep = new SharedObjectRef("std_err_keep").proxy<{ + get: () => Promise; + reset: () => void; + }>(); + + root_dir = new SharedObjectRef("root_dir").proxy<{ + get_file: (path_str: string) => Promise; + }>(); + + // llvm-tools + await term.writeln(`$${blueText} llvm-tools${resetText}`); + await llvm_tools(); + + // clang -h + await term.writeln(`\n$${blueText} clang --help${resetText}`); + await clang("--help"); + + // clang -v + await term.writeln(`\n$${blueText} clang -v${resetText}`); + await clang("-v"); + + // wasm-ld --help + await term.writeln(`\n$${blueText} wasm-ld --help${resetText}`); + await wasm_ld("--help"); + + // wasm-ld -v + await term.writeln(`\n$${blueText} wasm-ld -v${resetText}`); + await wasm_ld("-v"); + + // tree -h + await term.writeln(`\n$${blueText} tree -h${resetText}`); + await tree("-h"); + + // tree / + await term.writeln(`\n$${blueText} tree /${resetText}`); + await tree("/"); + + // rustc -h + await std_out_keep.reset(); + await std_err_keep.reset(); + await term.writeln(`\n$${blueText} rustc -h${resetText}`); + await rustc("-h"); + const rustc_help = await std_out_keep.get(); + const rustc_help_err = await std_err_keep.get(); + console.log(rustc_help); + console.warn(rustc_help_err); + + // rustc /hello.rs --sysroot /sysroot --target wasm32-wasip1-threads -Csave-temps --out-dir /tmp + await term.writeln( + `\n$${blueText} rustc /hello.rs --sysroot /sysroot --target wasm32-wasip1-threads -Csave-temps --out-dir /tmp${resetText}`, + ); + try { + await std_out_keep.reset(); + await std_err_keep.reset(); + await rustc( + "/hello.rs", + "--sysroot", + "/sysroot", + "--target", + "wasm32-wasip1-threads", + "-Csave-temps", + "--out-dir", + "/tmp", + ); + } catch (e) { + console.error(e); + } + const out = await std_out_keep.get(); + const err = await std_err_keep.get(); + console.log(out); + console.warn(err); + + // tree / + await term.writeln(`\n$${blueText} tree /${resetText}`); + await tree("/"); + + // If the glob pattern is used, + // it seems to be passed directly to path_open, + // so it is necessary to specify it carefully. + if (!err.includes("error: could not exec the linker")) { + throw new Error("cannot get lld arguments"); + } + // extract lld arguments line + const lld_args_and_etc = err + .split("\n") + .find((line) => line.includes("rust-lld") && line.includes("note: ")); + if (!lld_args_and_etc) { + throw new Error("cannot get lld arguments"); + } + const lld_args_str = lld_args_and_etc.split('"rust-lld"')[1].trim(); + const lld_args_with_wrap = lld_args_str.split(" "); + let lld_args = lld_args_with_wrap.map((arg) => arg.slice(1, -1)); + // rm -flavor wasm + lld_args = lld_args.filter((arg) => arg !== "-flavor" && arg !== "wasm"); + // rm -Wl + lld_args = lld_args.map((arg) => { + if (arg.includes("-Wl,")) { + return arg.slice(4); + } + return arg; + }); + console.log(lld_args); + + await term.writeln( + `\n$${blueText} wasm-ld ${lld_args.join(" ")}${resetText}`, + ); + try { + await wasm_ld(...lld_args); + } catch (error) { + console.error(error); + const redText = "\x1b[31m"; + const boldText = "\x1b[1m"; + + const message = `${boldText}${redText}Error: ${error.message}${resetText}\n`; + const stack = `${redText}Stack Trace: ${error.stack}${resetText}`; + + await term.writeln(message + stack); + } + + // tree / + await term.writeln(`\n$${blueText} tree /${resetText}`); + await tree("/"); + + // rustc_with_lld /hello.rs --sysroot /sysroot --target wasm32-wasip1 + await term.writeln( + `\n$${blueText} rustc_with_lld /hello.rs --sysroot /sysroot-with-lld --target wasm32-wasip1${resetText}`, + ); + try { + await rustc_with_lld( + "/hello.rs", + "--sysroot", + "/sysroot-with-lld", + "--target", + "wasm32-wasip1", + ); + } catch (e) { + console.error(e); + } + + // tree / + await term.writeln(`\n$${blueText} tree /${resetText}`); + await tree("/"); + + // run /hello.wasm + await term.writeln(`\n$${blueText} run /hello.wasm${resetText}`); + const created_wasm_buffer = await root_dir.get_file("hello.wasm"); + const created_wasm = await WebAssembly.compile(created_wasm_buffer); + const wasi = new WASIFarmAnimal( + wasi_refs, + [], // args + [], // env + ); + + // Memory is rewritten at this time. + const inst = (await WebAssembly.instantiate(created_wasm, { + wasi_snapshot_preview1: wasi.wasiImport, + })) as unknown as { + exports: { memory: WebAssembly.Memory; _start: () => unknown }; + }; + + wasi.start(inst); + + // all done + await term.writeln(`\n$${blueText} All done!${resetText}`); +}; diff --git a/threads/examples/wasi_workers/echo_and_rewrite.wasm b/threads/examples/wasi_workers/echo_and_rewrite.wasm new file mode 100644 index 0000000..1f4e413 Binary files /dev/null and b/threads/examples/wasi_workers/echo_and_rewrite.wasm differ diff --git a/threads/examples/wasi_workers/index.html b/threads/examples/wasi_workers/index.html new file mode 100644 index 0000000..f111ef3 --- /dev/null +++ b/threads/examples/wasi_workers/index.html @@ -0,0 +1,12 @@ + + + + + +

+ #### +

+ + + + diff --git a/threads/examples/wasi_workers/index.ts b/threads/examples/wasi_workers/index.ts new file mode 100644 index 0000000..0df777d --- /dev/null +++ b/threads/examples/wasi_workers/index.ts @@ -0,0 +1,41 @@ +import { + OpenFile, + File, + ConsoleStdout, + PreopenDirectory, + type Inode, + Directory, +} from "@bjorn3/browser_wasi_shim"; +import { WASIFarm } from "../../src"; + +const worker = new Worker("./worker.ts", { type: "module" }); +worker.onmessage = (event) => { + const { wasi_ref: wasi_ref2 } = event.data; + + (async () => { + const current_directory = new Map(); + current_directory.set( + "hello.txt", + new File(new TextEncoder().encode("Hello, world!")), + ); + current_directory.set("hello2", new Directory(new Map())); + + const wasi_farm = new WASIFarm( + new OpenFile(new File([])), // stdin + ConsoleStdout.lineBuffered((msg) => + console.log(`[WASI stdout on main thread] ${msg}`), + ), + ConsoleStdout.lineBuffered((msg) => console.warn(`[WASI stderr] ${msg}`)), + [new PreopenDirectory(".", current_directory)], + ); + console.log("WASI farm created"); + const wasi_ref = await wasi_farm.get_ref(); + + const myWorker1 = new Worker("./worker1.ts", { type: "module" }); + myWorker1.postMessage({ wasi_ref, wasi_ref2 }); + const myWorker2 = new Worker("./worker2.ts", { type: "module" }); + myWorker2.postMessage({ wasi_ref, wasi_ref2 }); + const myWorker3 = new Worker("./worker3.ts", { type: "module" }); + myWorker3.postMessage({ wasi_ref, wasi_ref2 }); + })(); +}; diff --git a/threads/examples/wasi_workers/main.rs b/threads/examples/wasi_workers/main.rs new file mode 100644 index 0000000..41a744c --- /dev/null +++ b/threads/examples/wasi_workers/main.rs @@ -0,0 +1,52 @@ +fn main() { + // ファイル名を受けとる + let args: Vec = std::env::args().collect(); + let filename = &args[1]; + + println!("reading file: {}", filename); + + // ファイルを読み込む + let file = std::fs::read_to_string(filename).expect("ファイルが読み込めませんでした"); + + // ファイルの内容を表示 + println!("{}", file); + + // ファイルの内容を書き換え + // 二つ目の引数の文字列を二つ目の文字列に書き換える + let replaced = file.replace(&args[2], &args[3]); + + // 書き換えた内容を表示 + println!("{}", replaced); + + println!("random replace start"); + + let start = std::env::args().nth(4).unwrap().parse::().unwrap(); + let end = std::env::args().nth(5).unwrap().parse::().unwrap(); + + // 新しいfileを作る + let new_file = format!("{}-{}~{}.txt", filename, start, end); + std::fs::write(&new_file, "$$$$$$$$$").expect("ファイルが書き込めませんでした"); + + let loop_n = std::env::args().nth(6).unwrap_or("100".to_string()).parse::().unwrap(); + + // loop { + for _ in 0..loop_n { + // ファイルを読み込む + let file = std::fs::read_to_string(filename).expect("ファイルが読み込めませんでした"); + + // ランダムな数値を生成 + let random = rand::random::() % (end - start) + start; + + // 生成した数値をkファイルの内容に書き換える + let replaced = format!("{}, {}", file, random); + + // 書き換えた内容を表示 + println!("{}", replaced); + + // ファイルを書き換える + std::fs::write(filename, &replaced).expect("ファイルが書き込めませんでした"); + + // 1秒待つ + // std::thread::sleep(std::time::Duration::from_secs(1)); + } +} diff --git a/threads/examples/wasi_workers/worker.ts b/threads/examples/wasi_workers/worker.ts new file mode 100644 index 0000000..849ed41 --- /dev/null +++ b/threads/examples/wasi_workers/worker.ts @@ -0,0 +1,23 @@ +import { + ConsoleStdout, + type Inode, + PreopenDirectory, + File, +} from "@bjorn3/browser_wasi_shim"; +import { WASIFarm } from "../../src"; + +const dir = new Map(); +dir.set("hello2.txt", new File(new TextEncoder().encode("Hello, world!!!!!"))); + +const wasi_farm = new WASIFarm( + undefined, + ConsoleStdout.lineBuffered((msg) => + console.log(`[WASI stdout on worker] ${msg}`), + ), + undefined, + [new PreopenDirectory("hello2", dir)], +); + +console.log("WASI farm created"); +const wasi_ref = await wasi_farm.get_ref(); +self.postMessage({ wasi_ref }); diff --git a/threads/examples/wasi_workers/worker1.ts b/threads/examples/wasi_workers/worker1.ts new file mode 100644 index 0000000..981b876 --- /dev/null +++ b/threads/examples/wasi_workers/worker1.ts @@ -0,0 +1,36 @@ +import { WASIFarmAnimal } from "../../src"; + +self.onmessage = async (e) => { + const { wasi_ref, wasi_ref2 } = e.data; + + console.dir(wasi_ref); + console.dir(wasi_ref2); + + const wasi = new WASIFarmAnimal( + [wasi_ref2, wasi_ref], + [ + "echo_and_rewrite", + "hello2/hello2.txt", + "world", + "new_world", + "0", + "100", + "100", + ], // args + [""], // env + // options + ); + + console.dir(wasi, { depth: null }); + + const wasm = await fetch("./echo_and_rewrite.wasm"); + const buff = await wasm.arrayBuffer(); + const { instance } = await WebAssembly.instantiate(buff, { + wasi_snapshot_preview1: wasi.wasiImport, + }); + wasi.start( + instance as unknown as { + exports: { memory: WebAssembly.Memory; _start: () => unknown }; + }, + ); +}; diff --git a/threads/examples/wasi_workers/worker2.ts b/threads/examples/wasi_workers/worker2.ts new file mode 100644 index 0000000..b93a128 --- /dev/null +++ b/threads/examples/wasi_workers/worker2.ts @@ -0,0 +1,22 @@ +import { WASIFarmAnimal } from "../../src"; + +self.onmessage = async (e) => { + const { wasi_ref, wasi_ref2 } = e.data; + + const wasi = new WASIFarmAnimal( + [wasi_ref2, wasi_ref], + ["echo_and_rewrite", "hello.txt", "world", "new_world", "100", "200"], // args + [""], // env + // options + ); + const wasm = await fetch("./echo_and_rewrite.wasm"); + const buff = await wasm.arrayBuffer(); + const { instance } = await WebAssembly.instantiate(buff, { + wasi_snapshot_preview1: wasi.wasiImport, + }); + wasi.start( + instance as unknown as { + exports: { memory: WebAssembly.Memory; _start: () => unknown }; + }, + ); +}; diff --git a/threads/examples/wasi_workers/worker3.ts b/threads/examples/wasi_workers/worker3.ts new file mode 100644 index 0000000..3862dcf --- /dev/null +++ b/threads/examples/wasi_workers/worker3.ts @@ -0,0 +1,22 @@ +import { WASIFarmAnimal } from "../../src"; + +self.onmessage = async (e) => { + const { wasi_ref, wasi_ref2 } = e.data; + + const wasi = new WASIFarmAnimal( + [wasi_ref, wasi_ref2], + ["echo_and_rewrite", "hello.txt", "world", "new_world", "200", "300"], // args + [""], // env + // options + ); + const wasm = await fetch("./echo_and_rewrite.wasm"); + const buff = await wasm.arrayBuffer(); + const { instance } = await WebAssembly.instantiate(buff, { + wasi_snapshot_preview1: wasi.wasiImport, + }); + wasi.start( + instance as unknown as { + exports: { memory: WebAssembly.Memory; _start: () => unknown }; + }, + ); +}; diff --git a/threads/examples/wasi_workers_rustc/index.html b/threads/examples/wasi_workers_rustc/index.html new file mode 100644 index 0000000..7f7413a --- /dev/null +++ b/threads/examples/wasi_workers_rustc/index.html @@ -0,0 +1,31 @@ + + + + + + + + + +
+
+

+ Note: the failure to invoke the linker at the end is expected. + WASI doesn't have a way to invoke external processes and rustc doesn't have a builtin linker. + This demo highlights how far `rustc` can get on this polyfill before failing due to other reasons. +

+ + + + diff --git a/threads/examples/wasi_workers_rustc/index.ts b/threads/examples/wasi_workers_rustc/index.ts new file mode 100644 index 0000000..4d9bf7a --- /dev/null +++ b/threads/examples/wasi_workers_rustc/index.ts @@ -0,0 +1,195 @@ +import { WASIFarm } from "../../src"; +import { + Directory, + Fd, + File, + type Inode, + PreopenDirectory, +} from "@bjorn3/browser_wasi_shim"; +import { Terminal } from "@xterm/xterm"; +import { FitAddon } from "xterm-addon-fit"; +import "@xterm/xterm/css/xterm.css"; + +const term = new Terminal({ + convertEol: true, +}); +const terminalElement = document.getElementById("terminal"); +if (!terminalElement) { + throw new Error("Terminal element not found"); +} +term.open(terminalElement); + +const fitAddon = new FitAddon(); +term.loadAddon(fitAddon); +fitAddon.fit(); + +class XtermStdio extends Fd { + /*:: term: Terminal*/ + term: Terminal; + + constructor(term: Terminal) { + super(); + this.term = term; + } + fd_write(data /*: Uint8Array*/) /*: {ret: number, nwritten: number}*/ { + console.log(data); + this.term.write(new TextDecoder().decode(data)); + return { ret: 0, nwritten: data.byteLength }; + } +} + +term.writeln("\x1B[93mDownloading\x1B[0m"); +//let wasm = await WebAssembly.compileStreaming(fetch("/rust_out.wasm")); +term.writeln("\x1B[93mInstantiating\x1B[0m"); + +async function load_external_file(path) { + return new File(await (await (await fetch(path)).blob()).arrayBuffer()); +} + +const toMap = (arr: Array<[string, Inode]>) => { + const map = new Map(); + for (const [key, value] of arr) { + map.set(key, value); + } + return map; +}; + +const fds = [ + new PreopenDirectory("/tmp", toMap([])), + new PreopenDirectory( + "/sysroot", + toMap([ + [ + "lib", + new Directory([ + [ + "rustlib", + new Directory([ + ["wasm32-wasi", new Directory([["lib", new Directory([])]])], + [ + "x86_64-unknown-linux-gnu", + new Directory([ + [ + "lib", + new Directory( + await (async () => { + const dir = new Map(); + for (const file of [ + "libaddr2line-b8754aeb03c02354.rlib", + "libadler-05c3545f6cd12159.rlib", + "liballoc-0dab879bc41cd6bd.rlib", + "libcfg_if-c7fd2cef50341546.rlib", + "libcompiler_builtins-a99947d020d809d6.rlib", + "libcore-4b8e8a815d049db3.rlib", + "libgetopts-bbb75529e85d129d.rlib", + "libgimli-598847d27d7a3cbf.rlib", + "libhashbrown-d2ff91fdf93cacb2.rlib", + "liblibc-dc63949c664c3fce.rlib", + "libmemchr-2d3a423be1a6cb96.rlib", + "libminiz_oxide-b109506a0ccc4c6a.rlib", + "libobject-7b48def7544c748b.rlib", + "libpanic_abort-c93441899b93b849.rlib", + "libpanic_unwind-11d9ba05b60bf694.rlib", + "libproc_macro-1a7f7840bb9983dc.rlib", + "librustc_demangle-59342a335246393d.rlib", + "librustc_std_workspace_alloc-552b185085090ff6.rlib", + "librustc_std_workspace_core-5d8a121daa7eeaa9.rlib", + "librustc_std_workspace_std-97f43841ce452f7d.rlib", + "libstd-bdedb7706a556da2.rlib", + "libstd-bdedb7706a556da2.so", + "libstd_detect-cca21eebc4281add.rlib", + "libsysroot-f654e185be3ffebd.rlib", + "libtest-f06fa3fbc201c558.rlib", + "libunicode_width-19a0dcd589fa0877.rlib", + "libunwind-747b693f90af9445.rlib", + ]) { + dir.set( + file, + await load_external_file( + `../wasi_multi_threads_rustc/rust_wasm/rustc_cranelift/dist/lib/rustlib/x86_64-unknown-linux-gnu/lib/${file}`, + ), + ); + } + return dir; + })(), + ), + ], + ]), + ], + ]), + ], + ]), + ], + ]), + ), + new PreopenDirectory( + "/", + toMap([ + [ + "hello.rs", + new File( + new TextEncoder().encode(`fn main() { println!("Hello World!"); }`), + ), + ], + ]), + ), +]; + +const wasi_farm = new WASIFarm( + new XtermStdio(term), + new XtermStdio(term), + new XtermStdio(term), + fds, + { + allocator_size: 1024 * 1024 * 1024, + }, +); + +const wasi_ref = await wasi_farm.get_ref(); + +const worker = new Worker("./worker.ts", { type: "module" }); +worker.postMessage({ wasi_ref }); + +console.log("worker created", worker); + +worker.onmessage = (event) => { + const data = event.data; + const { term: write } = data; + + term.writeln(write); +}; + +const downloadsElement = document.getElementById("downloads"); + +if (!downloadsElement) { + throw new Error("Downloads element not found"); +} + +let content: Inode | undefined; +while (true) { + content = fds[2].dir.contents.get( + "hello.hello.65c991d23c885d45-cgu.0.rcgu.o", + ); + if (content) { + break; + } + await new Promise((resolve) => setTimeout(resolve, 1000)); +} +await new Promise((resolve) => setTimeout(resolve, 1000)); +downloadsElement.innerHTML += `
Download object`; +let content2: Inode | undefined; +while (true) { + content2 = fds[2].dir.contents.get("hello.allocator_shim.rcgu.o"); + if (content2) { + break; + } + await new Promise((resolve) => setTimeout(resolve, 1000)); +} +await new Promise((resolve) => setTimeout(resolve, 1000)); +downloadsElement.innerHTML += `
Download allocator shim`; diff --git a/threads/examples/wasi_workers_rustc/worker.ts b/threads/examples/wasi_workers_rustc/worker.ts new file mode 100644 index 0000000..9b8e2ee --- /dev/null +++ b/threads/examples/wasi_workers_rustc/worker.ts @@ -0,0 +1,76 @@ +import { strace } from "@bjorn3/browser_wasi_shim"; +import { WASIFarmAnimal } from "../../src"; + +console.log("worker.js"); + +globalThis.onmessage = async (e) => { + const wasm = await WebAssembly.compileStreaming( + fetch( + "../wasi_multi_threads_rustc/rust_wasm/rustc_cranelift/rustc_opt.wasm", + ), + ); + + console.log("worker.js onmessage", e.data); + + const { wasi_ref } = e.data; + + const args = [ + "rustc", + "/hello.rs", + "--sysroot", + "/sysroot", + "--target", + "x86_64-unknown-linux-gnu", + "-Cpanic=abort", + "-Ccodegen-units=1", + ]; + const env = ["RUSTC_LOG=info"]; + + console.log("wasi_ref", wasi_ref); + + const w = new WASIFarmAnimal(wasi_ref, args, env, {}); + + let next_thread_id = 1; + + const inst = (await WebAssembly.instantiate(wasm, { + env: { + memory: new WebAssembly.Memory({ + initial: 256, + maximum: 16384, + shared: true, + }), + }, + wasi: { + "thread-spawn": (start_arg) => { + console.log("thread-spawn", start_arg); + + const thread_id = next_thread_id++; + inst.exports.wasi_thread_start(thread_id, start_arg); + return thread_id; + }, + }, + wasi_snapshot_preview1: strace(w.wasiImport, ["fd_prestat_get"]), + })) as unknown as { + exports: { + wasi_thread_start: (thread_id: number, start_arg: number) => void; + memory: WebAssembly.Memory; + _start: () => unknown; + }; + }; + + postMessage({ + term: "\x1B[93mExecuting\x1B[0m", + }); + console.log(inst.exports); + try { + w.start(inst); + } catch (e) { + postMessage({ + term: `Exception: ${e.message}`, + }); + // /*term.writeln("backtrace:"); term.writeln(e.stack);*/ + } + postMessage({ + term: "\x1B[92mDone\x1B[0m", + }); +}; diff --git a/threads/examples/wasi_workers_single/echo_and_rewrite.wasm b/threads/examples/wasi_workers_single/echo_and_rewrite.wasm new file mode 100644 index 0000000..d03ed2d Binary files /dev/null and b/threads/examples/wasi_workers_single/echo_and_rewrite.wasm differ diff --git a/threads/examples/wasi_workers_single/index.html b/threads/examples/wasi_workers_single/index.html new file mode 100644 index 0000000..c911479 --- /dev/null +++ b/threads/examples/wasi_workers_single/index.html @@ -0,0 +1,11 @@ + + + + + +

+ #### +

+ + + diff --git a/threads/examples/wasi_workers_single/index.ts b/threads/examples/wasi_workers_single/index.ts new file mode 100644 index 0000000..255d858 --- /dev/null +++ b/threads/examples/wasi_workers_single/index.ts @@ -0,0 +1,43 @@ +import { + OpenFile, + File, + WASI, + ConsoleStdout, + PreopenDirectory, +} from "@bjorn3/browser_wasi_shim"; + +// sleep 1000 +await new Promise((resolve) => setTimeout(resolve, 1000)); + +const toMap = (arr: Array<[string, File]>) => new Map(arr); + +const wasi = new WASI( + ["echo_and_rewrite", "hello.txt", "world", "new_world"], // args + ["FOO=bar"], // env + [ + new OpenFile(new File([])), // stdin + ConsoleStdout.lineBuffered((msg) => console.log(`[WASI stdout] ${msg}`)), + ConsoleStdout.lineBuffered((msg) => console.warn(`[WASI stderr] ${msg}`)), + new PreopenDirectory( + ".", + toMap([ + ["hello.txt", new File(new TextEncoder().encode("Hello, world!"))], + ]), + ), + ], + { debug: true }, +); +const wasm = await fetch("./echo_and_rewrite.wasm"); +const buff = await wasm.arrayBuffer(); +const { instance } = await WebAssembly.instantiate(buff, { + wasi_snapshot_preview1: wasi.wasiImport, +}); + +// sleep 1000ms +await new Promise((resolve) => setTimeout(resolve, 1000)); + +wasi.start( + instance as unknown as { + exports: { memory: WebAssembly.Memory; _start: () => unknown }; + }, +); diff --git a/threads/import-module-test/.gitignore b/threads/import-module-test/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/threads/import-module-test/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/threads/import-module-test/index.html b/threads/import-module-test/index.html new file mode 100644 index 0000000..44a9335 --- /dev/null +++ b/threads/import-module-test/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + TS + + +
+ + + diff --git a/threads/import-module-test/package-lock.json b/threads/import-module-test/package-lock.json new file mode 100644 index 0000000..c8be983 --- /dev/null +++ b/threads/import-module-test/package-lock.json @@ -0,0 +1,902 @@ +{ + "name": "import-module-test", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "import-module-test", + "version": "0.0.0", + "dependencies": { + "@oligami/browser_wasi_shim-threads": "file:..", + "import-module-test": "file:" + }, + "devDependencies": { + "typescript": "^5.5.3", + "vite": "^5.4.8" + } + }, + "..": { + "name": "@oligami/browser_wasi_shim-threads", + "version": "0.1.0", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@bjorn3/browser_wasi_shim": "^0.3.0", + "@oligami/browser_wasi_shim-threads": "file:" + }, + "devDependencies": { + "@biomejs/biome": "1.9.3", + "@swc/cli": "^0.4.0", + "@swc/core": "^1.7.28", + "better-typescript-lib": "^2.8.0", + "npm-watch": "^0.13.0", + "typescript": "^5.6.2", + "unplugin-swc": "^1.5.1", + "vite": "^5.4.8", + "vite-plugin-dts": "^4.2.3" + }, + "peerDependencies": { + "@bjorn3/browser_wasi_shim": "^0.3.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@oligami/browser_wasi_shim-threads": { + "resolved": "..", + "link": true + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", + "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", + "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", + "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", + "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", + "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", + "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", + "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", + "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", + "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", + "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", + "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", + "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", + "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", + "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", + "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", + "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/import-module-test": { + "resolved": "", + "link": true + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", + "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.24.0", + "@rollup/rollup-android-arm64": "4.24.0", + "@rollup/rollup-darwin-arm64": "4.24.0", + "@rollup/rollup-darwin-x64": "4.24.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", + "@rollup/rollup-linux-arm-musleabihf": "4.24.0", + "@rollup/rollup-linux-arm64-gnu": "4.24.0", + "@rollup/rollup-linux-arm64-musl": "4.24.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", + "@rollup/rollup-linux-riscv64-gnu": "4.24.0", + "@rollup/rollup-linux-s390x-gnu": "4.24.0", + "@rollup/rollup-linux-x64-gnu": "4.24.0", + "@rollup/rollup-linux-x64-musl": "4.24.0", + "@rollup/rollup-win32-arm64-msvc": "4.24.0", + "@rollup/rollup-win32-ia32-msvc": "4.24.0", + "@rollup/rollup-win32-x64-msvc": "4.24.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/typescript": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vite": { + "version": "5.4.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", + "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + } + } +} diff --git a/threads/import-module-test/package.json b/threads/import-module-test/package.json new file mode 100644 index 0000000..7b6191b --- /dev/null +++ b/threads/import-module-test/package.json @@ -0,0 +1,19 @@ +{ + "name": "import-module-test", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "devDependencies": { + "typescript": "^5.5.3", + "vite": "^5.4.8" + }, + "dependencies": { + "@oligami/browser_wasi_shim-threads": "file:..", + "import-module-test": "file:" + } +} diff --git a/threads/import-module-test/public/vite.svg b/threads/import-module-test/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/threads/import-module-test/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/threads/import-module-test/src/counter.ts b/threads/import-module-test/src/counter.ts new file mode 100644 index 0000000..86cbb7d --- /dev/null +++ b/threads/import-module-test/src/counter.ts @@ -0,0 +1,9 @@ +export function setupCounter(element: HTMLButtonElement) { + let counter = 0; + const setCounter = (count: number) => { + counter = count; + element.innerHTML = `count is ${counter}`; + }; + element.addEventListener("click", () => setCounter(counter + 1)); + setCounter(0); +} diff --git a/threads/import-module-test/src/main.ts b/threads/import-module-test/src/main.ts new file mode 100644 index 0000000..6612535 --- /dev/null +++ b/threads/import-module-test/src/main.ts @@ -0,0 +1,32 @@ +import "./style.css"; +import typescriptLogo from "./typescript.svg"; +import viteLogo from "/vite.svg"; +import { setupCounter } from "./counter.ts"; + +// biome-ignore lint/style/noNonNullAssertion: +document.querySelector("#app")!.innerHTML = ` +
+ + + + + + +

Vite + TypeScript

+
+ +
+

+ Click on the Vite and TypeScript logos to learn more +

+
+`; + +// biome-ignore lint/style/noNonNullAssertion: +setupCounter(document.querySelector("#counter")!); + +import { WASIFarm } from "@oligami/browser_wasi_shim-threads"; + +// Create a new WASI farm +const farm = new WASIFarm(); +console.dir(farm, { depth: null }); diff --git a/threads/import-module-test/src/style.css b/threads/import-module-test/src/style.css new file mode 100644 index 0000000..f9c7350 --- /dev/null +++ b/threads/import-module-test/src/style.css @@ -0,0 +1,96 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.vanilla:hover { + filter: drop-shadow(0 0 2em #3178c6aa); +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/threads/import-module-test/src/typescript.svg b/threads/import-module-test/src/typescript.svg new file mode 100644 index 0000000..d91c910 --- /dev/null +++ b/threads/import-module-test/src/typescript.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/threads/import-module-test/src/vite-env.d.ts b/threads/import-module-test/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/threads/import-module-test/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/threads/import-module-test/tsconfig.json b/threads/import-module-test/tsconfig.json new file mode 100644 index 0000000..0511b9f --- /dev/null +++ b/threads/import-module-test/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/threads/import-module-test/vite.config.ts b/threads/import-module-test/vite.config.ts new file mode 100644 index 0000000..a5eb955 --- /dev/null +++ b/threads/import-module-test/vite.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from "vite"; + +export default defineConfig({ + server: { + headers: { + "Cross-Origin-Embedder-Policy": "require-corp", + "Cross-Origin-Opener-Policy": "same-origin", + }, + }, +}); diff --git a/threads/index.html b/threads/index.html new file mode 100644 index 0000000..9fe0595 --- /dev/null +++ b/threads/index.html @@ -0,0 +1,48 @@ + + + + + + Examples + + + + +

all link

+ + + + diff --git a/threads/package-lock.json b/threads/package-lock.json new file mode 100644 index 0000000..2bb48d4 --- /dev/null +++ b/threads/package-lock.json @@ -0,0 +1,4635 @@ +{ + "name": "@oligami/browser_wasi_shim-threads", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@oligami/browser_wasi_shim-threads", + "version": "0.1.0", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@bjorn3/browser_wasi_shim": "^0.3.0", + "@oligami/browser_wasi_shim-threads": "file:" + }, + "devDependencies": { + "@biomejs/biome": "1.9.3", + "@swc/cli": "^0.4.0", + "@swc/core": "^1.7.28", + "better-typescript-lib": "^2.8.0", + "npm-watch": "^0.13.0", + "typescript": "^5.6.2", + "unplugin-swc": "^1.5.1", + "vite": "^5.4.8", + "vite-plugin-dts": "^4.2.3" + }, + "peerDependencies": { + "@bjorn3/browser_wasi_shim": "^0.3.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", + "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", + "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.7.tgz", + "integrity": "sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.7.tgz", + "integrity": "sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@biomejs/biome": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.3.tgz", + "integrity": "sha512-POjAPz0APAmX33WOQFGQrwLvlu7WLV4CFJMlB12b6ZSg+2q6fYu9kZwLCOA+x83zXfcPd1RpuWOKJW0GbBwLIQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "1.9.3", + "@biomejs/cli-darwin-x64": "1.9.3", + "@biomejs/cli-linux-arm64": "1.9.3", + "@biomejs/cli-linux-arm64-musl": "1.9.3", + "@biomejs/cli-linux-x64": "1.9.3", + "@biomejs/cli-linux-x64-musl": "1.9.3", + "@biomejs/cli-win32-arm64": "1.9.3", + "@biomejs/cli-win32-x64": "1.9.3" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.3.tgz", + "integrity": "sha512-QZzD2XrjJDUyIZK+aR2i5DDxCJfdwiYbUKu9GzkCUJpL78uSelAHAPy7m0GuPMVtF/Uo+OKv97W3P9nuWZangQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.3.tgz", + "integrity": "sha512-vSCoIBJE0BN3SWDFuAY/tRavpUtNoqiceJ5PrU3xDfsLcm/U6N93JSM0M9OAiC/X7mPPfejtr6Yc9vSgWlEgVw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.3.tgz", + "integrity": "sha512-vJkAimD2+sVviNTbaWOGqEBy31cW0ZB52KtpVIbkuma7PlfII3tsLhFa+cwbRAcRBkobBBhqZ06hXoZAN8NODQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.3.tgz", + "integrity": "sha512-VBzyhaqqqwP3bAkkBrhVq50i3Uj9+RWuj+pYmXrMDgjS5+SKYGE56BwNw4l8hR3SmYbLSbEo15GcV043CDSk+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.3.tgz", + "integrity": "sha512-x220V4c+romd26Mu1ptU+EudMXVS4xmzKxPVb9mgnfYlN4Yx9vD5NZraSx/onJnd3Gh/y8iPUdU5CDZJKg9COA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.3.tgz", + "integrity": "sha512-TJmnOG2+NOGM72mlczEsNki9UT+XAsMFAOo8J0me/N47EJ/vkLXxf481evfHLlxMejTY6IN8SdRSiPVLv6AHlA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.3.tgz", + "integrity": "sha512-lg/yZis2HdQGsycUvHWSzo9kOvnGgvtrYRgoCEwPBwwAL8/6crOp3+f47tPwI/LI1dZrhSji7PNsGKGHbwyAhw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.3.tgz", + "integrity": "sha512-cQMy2zanBkVLpmmxXdK6YePzmZx0s5Z7KEnwmrW54rcXK3myCNbQa09SwGZ8i/8sLw0H9F3X7K4rxVNGU8/D4Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@bjorn3/browser_wasi_shim": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@bjorn3/browser_wasi_shim/-/browser_wasi_shim-0.3.0.tgz", + "integrity": "sha512-FlRBYttPRLcWORzBe6g8nmYTafBkOEFeOqMYM4tAHJzFsQy4+xJA94z85a9BCs8S+Uzfh9LrkpII7DXr2iUVFg==", + "license": "MIT OR Apache-2.0" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/api-extractor": { + "version": "7.47.7", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.47.7.tgz", + "integrity": "sha512-fNiD3G55ZJGhPOBPMKD/enozj8yxJSYyVJWxRWdcUtw842rvthDHJgUWq9gXQTensFlMHv2wGuCjjivPv53j0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/api-extractor-model": "7.29.6", + "@microsoft/tsdoc": "~0.15.0", + "@microsoft/tsdoc-config": "~0.17.0", + "@rushstack/node-core-library": "5.7.0", + "@rushstack/rig-package": "0.5.3", + "@rushstack/terminal": "0.14.0", + "@rushstack/ts-command-line": "4.22.6", + "lodash": "~4.17.15", + "minimatch": "~3.0.3", + "resolve": "~1.22.1", + "semver": "~7.5.4", + "source-map": "~0.6.1", + "typescript": "5.4.2" + }, + "bin": { + "api-extractor": "bin/api-extractor" + } + }, + "node_modules/@microsoft/api-extractor-model": { + "version": "7.29.6", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.29.6.tgz", + "integrity": "sha512-gC0KGtrZvxzf/Rt9oMYD2dHvtN/1KPEYsrQPyMKhLHnlVuO/f4AFN3E4toqZzD2pt4LhkKoYmL2H9tX3yCOyRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "~0.15.0", + "@microsoft/tsdoc-config": "~0.17.0", + "@rushstack/node-core-library": "5.7.0" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/typescript": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/@microsoft/tsdoc": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.0.tgz", + "integrity": "sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.17.0.tgz", + "integrity": "sha512-v/EYRXnCAIHxOHW+Plb6OWuUoMotxTN0GLatnpOb1xq0KuTNw/WI3pamJx/UbsoJP5k9MCw1QxvvhPcF9pH3Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "0.15.0", + "ajv": "~8.12.0", + "jju": "~1.4.0", + "resolve": "~1.22.2" + } + }, + "node_modules/@mole-inc/bin-wrapper": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@mole-inc/bin-wrapper/-/bin-wrapper-8.0.1.tgz", + "integrity": "sha512-sTGoeZnjI8N4KS+sW2AN95gDBErhAguvkw/tWdCjeM8bvxpz5lqrnd0vOJABA1A+Ic3zED7PYoLP/RANLgVotA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bin-check": "^4.1.0", + "bin-version-check": "^5.0.0", + "content-disposition": "^0.5.4", + "ext-name": "^5.0.0", + "file-type": "^17.1.6", + "filenamify": "^5.0.2", + "got": "^11.8.5", + "os-filter-obj": "^2.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/@napi-rs/nice": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.0.1.tgz", + "integrity": "sha512-zM0mVWSXE0a0h9aKACLwKmD6nHcRiKrPpCfvaKqG1CqDEyjEawId0ocXxVzPMCAm6kkWr2P025msfxXEnt8UGQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/nice-android-arm-eabi": "1.0.1", + "@napi-rs/nice-android-arm64": "1.0.1", + "@napi-rs/nice-darwin-arm64": "1.0.1", + "@napi-rs/nice-darwin-x64": "1.0.1", + "@napi-rs/nice-freebsd-x64": "1.0.1", + "@napi-rs/nice-linux-arm-gnueabihf": "1.0.1", + "@napi-rs/nice-linux-arm64-gnu": "1.0.1", + "@napi-rs/nice-linux-arm64-musl": "1.0.1", + "@napi-rs/nice-linux-ppc64-gnu": "1.0.1", + "@napi-rs/nice-linux-riscv64-gnu": "1.0.1", + "@napi-rs/nice-linux-s390x-gnu": "1.0.1", + "@napi-rs/nice-linux-x64-gnu": "1.0.1", + "@napi-rs/nice-linux-x64-musl": "1.0.1", + "@napi-rs/nice-win32-arm64-msvc": "1.0.1", + "@napi-rs/nice-win32-ia32-msvc": "1.0.1", + "@napi-rs/nice-win32-x64-msvc": "1.0.1" + } + }, + "node_modules/@napi-rs/nice-android-arm-eabi": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.0.1.tgz", + "integrity": "sha512-5qpvOu5IGwDo7MEKVqqyAxF90I6aLj4n07OzpARdgDRfz8UbBztTByBp0RC59r3J1Ij8uzYi6jI7r5Lws7nn6w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-android-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.0.1.tgz", + "integrity": "sha512-GqvXL0P8fZ+mQqG1g0o4AO9hJjQaeYG84FRfZaYjyJtZZZcMjXW5TwkL8Y8UApheJgyE13TQ4YNUssQaTgTyvA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-arm64/-/nice-darwin-arm64-1.0.1.tgz", + "integrity": "sha512-91k3HEqUl2fsrz/sKkuEkscj6EAj3/eZNCLqzD2AA0TtVbkQi8nqxZCZDMkfklULmxLkMxuUdKe7RvG/T6s2AA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.0.1.tgz", + "integrity": "sha512-jXnMleYSIR/+TAN/p5u+NkCA7yidgswx5ftqzXdD5wgy/hNR92oerTXHc0jrlBisbd7DpzoaGY4cFD7Sm5GlgQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-freebsd-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.0.1.tgz", + "integrity": "sha512-j+iJ/ezONXRQsVIB/FJfwjeQXX7A2tf3gEXs4WUGFrJjpe/z2KB7sOv6zpkm08PofF36C9S7wTNuzHZ/Iiccfw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm-gnueabihf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.0.1.tgz", + "integrity": "sha512-G8RgJ8FYXYkkSGQwywAUh84m946UTn6l03/vmEXBYNJxQJcD+I3B3k5jmjFG/OPiU8DfvxutOP8bi+F89MCV7Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.0.1.tgz", + "integrity": "sha512-IMDak59/W5JSab1oZvmNbrms3mHqcreaCeClUjwlwDr0m3BoR09ZiN8cKFBzuSlXgRdZ4PNqCYNeGQv7YMTjuA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-musl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.0.1.tgz", + "integrity": "sha512-wG8fa2VKuWM4CfjOjjRX9YLIbysSVV1S3Kgm2Fnc67ap/soHBeYZa6AGMeR5BJAylYRjnoVOzV19Cmkco3QEPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-ppc64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.0.1.tgz", + "integrity": "sha512-lxQ9WrBf0IlNTCA9oS2jg/iAjQyTI6JHzABV664LLrLA/SIdD+I1i3Mjf7TsnoUbgopBcCuDztVLfJ0q9ubf6Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-riscv64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.0.1.tgz", + "integrity": "sha512-3xs69dO8WSWBb13KBVex+yvxmUeEsdWexxibqskzoKaWx9AIqkMbWmE2npkazJoopPKX2ULKd8Fm9veEn0g4Ig==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-s390x-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.0.1.tgz", + "integrity": "sha512-lMFI3i9rlW7hgToyAzTaEybQYGbQHDrpRkg+1gJWEpH0PLAQoZ8jiY0IzakLfNWnVda1eTYYlxxFYzW8Rqczkg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.0.1.tgz", + "integrity": "sha512-XQAJs7DRN2GpLN6Fb+ZdGFeYZDdGl2Fn3TmFlqEL5JorgWKrQGRUrpGKbgZ25UeZPILuTKJ+OowG2avN8mThBA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-musl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.0.1.tgz", + "integrity": "sha512-/rodHpRSgiI9o1faq9SZOp/o2QkKQg7T+DK0R5AkbnI/YxvAIEHf2cngjYzLMQSQgUhxym+LFr+UGZx4vK4QdQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-arm64-msvc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.0.1.tgz", + "integrity": "sha512-rEcz9vZymaCB3OqEXoHnp9YViLct8ugF+6uO5McifTedjq4QMQs3DHz35xBEGhH3gJWEsXMUbzazkz5KNM5YUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-ia32-msvc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.0.1.tgz", + "integrity": "sha512-t7eBAyPUrWL8su3gDxw9xxxqNwZzAqKo0Szv3IjVQd1GpXXVkb6vBBQUuxfIYaXMzZLwlxRQ7uzM2vdUE9ULGw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-x64-msvc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.0.1.tgz", + "integrity": "sha512-JlF+uDcatt3St2ntBG8H02F1mM45i5SF9W+bIKiReVE6wiy3o16oBP/yxt+RZ+N6LbCImJXJ6bXNO2kn9AXicg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@oligami/browser_wasi_shim-threads": { + "resolved": "", + "link": true + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.2.tgz", + "integrity": "sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", + "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", + "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", + "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", + "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", + "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", + "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", + "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", + "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", + "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", + "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", + "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", + "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", + "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", + "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", + "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", + "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rushstack/node-core-library": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.7.0.tgz", + "integrity": "sha512-Ff9Cz/YlWu9ce4dmqNBZpA45AEya04XaBFIjV7xTVeEf+y/kTjEasmozqFELXlNG4ROdevss75JrrZ5WgufDkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "~8.13.0", + "ajv-draft-04": "~1.0.0", + "ajv-formats": "~3.0.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.22.1", + "semver": "~7.5.4" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/node-core-library/node_modules/ajv": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/@rushstack/rig-package": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.3.tgz", + "integrity": "sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "~1.22.1", + "strip-json-comments": "~3.1.1" + } + }, + "node_modules/@rushstack/terminal": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.14.0.tgz", + "integrity": "sha512-juTKMAMpTIJKudeFkG5slD8Z/LHwNwGZLtU441l/u82XdTBfsP+LbGKJLCNwP5se+DMCT55GB8x9p6+C4UL7jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rushstack/node-core-library": "5.7.0", + "supports-color": "~8.1.1" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/terminal/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@rushstack/terminal/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@rushstack/ts-command-line": { + "version": "4.22.6", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.22.6.tgz", + "integrity": "sha512-QSRqHT/IfoC5nk9zn6+fgyqOPXHME0BfchII9EUPR19pocsNp/xSbeBCbD3PIR2Lg+Q5qk7OFqk1VhWPMdKHJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rushstack/terminal": "0.14.0", + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "string-argv": "~0.3.1" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@swc/cli": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@swc/cli/-/cli-0.4.0.tgz", + "integrity": "sha512-4JdVrPtF/4rCMXp6Q1h5I6YkYZrCCcqod7Wk97ZQq7K8vNGzJUryBv4eHCvqx5sJOJBrbYm9fcswe1B0TygNoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mole-inc/bin-wrapper": "^8.0.1", + "@swc/counter": "^0.1.3", + "commander": "^8.3.0", + "fast-glob": "^3.2.5", + "minimatch": "^9.0.3", + "piscina": "^4.3.0", + "semver": "^7.3.8", + "slash": "3.0.0", + "source-map": "^0.7.3" + }, + "bin": { + "spack": "bin/spack.js", + "swc": "bin/swc.js", + "swcx": "bin/swcx.js" + }, + "engines": { + "node": ">= 16.14.0" + }, + "peerDependencies": { + "@swc/core": "^1.2.66", + "chokidar": "^3.5.1" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@swc/core": { + "version": "1.7.28", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.7.28.tgz", + "integrity": "sha512-XapcMgsOS0cKh01AFEj+qXOk6KM4NZhp7a5vPicdhkRR8RzvjrCa7DTtijMxfotU8bqaEHguxmiIag2HUlT8QQ==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.12" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.7.28", + "@swc/core-darwin-x64": "1.7.28", + "@swc/core-linux-arm-gnueabihf": "1.7.28", + "@swc/core-linux-arm64-gnu": "1.7.28", + "@swc/core-linux-arm64-musl": "1.7.28", + "@swc/core-linux-x64-gnu": "1.7.28", + "@swc/core-linux-x64-musl": "1.7.28", + "@swc/core-win32-arm64-msvc": "1.7.28", + "@swc/core-win32-ia32-msvc": "1.7.28", + "@swc/core-win32-x64-msvc": "1.7.28" + }, + "peerDependencies": { + "@swc/helpers": "*" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.7.28", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.28.tgz", + "integrity": "sha512-BNkj6enHo2pdzOpCtQGKZbXT2A/qWIr0CVtbTM4WkJ3MCK/glbFsyO6X59p1r8+gfaZG4bWYnTTu+RuUAcsL5g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.7.28", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.7.28.tgz", + "integrity": "sha512-96zQ+X5Fd6P/RNPkOyikTJgEc2M4TzznfYvjRd2hye5h22jhxCLL/csoauDgN7lYfd7mwsZ/sVXwJTMKl+vZSA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.7.28", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.28.tgz", + "integrity": "sha512-l2100Wx6LdXMOmOW3+KoHhBhyZrGdz8ylkygcVOC0QHp6YIATfuG+rRHksfyEWCSOdL3anM9MJZJX26KT/s+XQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.7.28", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.28.tgz", + "integrity": "sha512-03m6iQ5Bv9u2VPnNRyaBmE8eHi056eE39L0gXcqGoo46GAGuoqYHt9pDz8wS6EgoN4t85iBMUZrkCNqFKkN6ZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.7.28", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.28.tgz", + "integrity": "sha512-vqVOpG/jc8mvTKQjaPBLhr7tnWyzuztOHsPnJqMWmg7zGcMeQC/2c5pU4uzRAfXMTp25iId6s4Y4wWfPS1EeDw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.7.28", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.28.tgz", + "integrity": "sha512-HGwpWuB83Kr+V0E+zT5UwIIY9OxiS8aLd0UVMRVWuO8SrQyKm9HKJ46+zoAb8tfJrpZftfxvbn2ayZWR7gqosA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.7.28", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.28.tgz", + "integrity": "sha512-q2Y2T8y8EgFtIiRyInnAXNe94aaHX74F0ha1Bl9VdRxE0u1/So+3VLbPvtp4V3Z6pj5pOePfCQJKifnllgAQ9A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.7.28", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.28.tgz", + "integrity": "sha512-bCqh4uBT/59h3dWK1v91In6qzz8rKoWoFRxCtNQLIK4jP55K0U231ZK9oN7neZD6bzcOUeFvOGgcyMAgDfFWfA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.7.28", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.28.tgz", + "integrity": "sha512-XTHbHrksnrqK3JSJ2sbuMWvdJ6/G0roRpgyVTmNDfhTYPOwcVaL/mSrPGLwbksYUbq7ckwoKzrobhdxvQzPsDA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.7.28", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.28.tgz", + "integrity": "sha512-jyXeoq6nX8abiCy2EpporsC5ywNENs4ocYuvxo1LSxDktWN1E2MTXq3cdJcEWB2Vydxq0rDcsGyzkRPMzFhkZw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@swc/types": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.12.tgz", + "integrity": "sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/argparse": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript/lib-decorators": { + "name": "@better-typescript-lib/decorators", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@better-typescript-lib/decorators/-/decorators-2.9.0.tgz", + "integrity": "sha512-l7JqR0EFD01ucYRILPuhcdxCcST+f3dSMj+/39gqjl1RdAIWtRGV2K2DOsqW4ye++A39In12++7LnaZLQZEVUQ==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "typescript": ">=4.5.2" + } + }, + "node_modules/@typescript/lib-dom": { + "name": "@better-typescript-lib/dom", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@better-typescript-lib/dom/-/dom-2.9.0.tgz", + "integrity": "sha512-UONr6zeUg5Inl4cNoSK9FgW2Hc0tjACSxKWs2ftgdaCmdokd4hZdqgEBiDyP5Xynx5UECNXA7krLGbHi9VUR3w==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "typescript": ">=4.5.2" + } + }, + "node_modules/@typescript/lib-es2015": { + "name": "@better-typescript-lib/es2015", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@better-typescript-lib/es2015/-/es2015-2.9.0.tgz", + "integrity": "sha512-l/2PVV8bE/1jg0lvdodrxXX/vu2iiPNUjkcyuhXMLIKk4r48Pka1G87TisXtF4fptWEcqrry9C2fSuwVj59jSA==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "typescript": ">=4.5.2" + } + }, + "node_modules/@typescript/lib-es2016": { + "name": "@better-typescript-lib/es2016", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@better-typescript-lib/es2016/-/es2016-2.9.0.tgz", + "integrity": "sha512-87jtcF08MkmJe3+hTjhP/SPz167O5A6PyldG8+1jOvEA23aUNTezpXEt0Y91g295qGwg6U3ATyOcJ6h5PzZEYw==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "typescript": ">=4.5.2" + } + }, + "node_modules/@typescript/lib-es2017": { + "name": "@better-typescript-lib/es2017", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@better-typescript-lib/es2017/-/es2017-2.9.0.tgz", + "integrity": "sha512-245luMwR3nBlg/q2qzqnkqS/ZbJuNoeAVeriAu4QzUwIXDQxBteawJwiB8qO8EgYgAWhvPJUZzhqUJyYrCBAtQ==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "typescript": ">=4.5.2" + } + }, + "node_modules/@typescript/lib-es2018": { + "name": "@better-typescript-lib/es2018", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@better-typescript-lib/es2018/-/es2018-2.9.0.tgz", + "integrity": "sha512-6pqeRyMwg2ub5VxY3TA7IbfqTNxY1oVw06DEskpnXuvsA5lSDhZVQxhegCryf8dsRbL2SY+JsDvqGPz2BB8qYA==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "typescript": ">=4.5.2" + } + }, + "node_modules/@typescript/lib-es2019": { + "name": "@better-typescript-lib/es2019", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@better-typescript-lib/es2019/-/es2019-2.9.0.tgz", + "integrity": "sha512-8iT42/M3E93gsAL9wsB+nU39UBe1syLUHfxoGdlx9zLZw8vNnLxKmpFTO2vC2BVBWTTB5ZkLKtlg6+UaYiqQZg==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "typescript": ">=4.5.2" + } + }, + "node_modules/@typescript/lib-es2020": { + "name": "@better-typescript-lib/es2020", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@better-typescript-lib/es2020/-/es2020-2.9.0.tgz", + "integrity": "sha512-1j4kkZQALxXRZ4yxszf8+GfrILq+Gt4cFNWTfLzdlm5cGmoWWbWci81HsudbvveWqfNKaSKcQIY5Z9bVrQCyeg==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "typescript": ">=4.5.2" + } + }, + "node_modules/@typescript/lib-es2021": { + "name": "@better-typescript-lib/es2021", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@better-typescript-lib/es2021/-/es2021-2.9.0.tgz", + "integrity": "sha512-mA1VcDvXzkekL646fZ/AazRQwuG6reyuHf3kL5B5Wn/HeihfwHh96FGSblaQWyDwoTZjUTP0s9m7Mtj5SgGxGQ==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "typescript": ">=4.5.2" + } + }, + "node_modules/@typescript/lib-es2022": { + "name": "@better-typescript-lib/es2022", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@better-typescript-lib/es2022/-/es2022-2.9.0.tgz", + "integrity": "sha512-iDUAglx7axyGVlGrEk6gnzIYZK4drnu59OvHFieeg2dMyc1EJEedsmL9jK9XJcahZtpt/JEPqWJnE5os+N4Rgw==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "typescript": ">=4.5.2" + } + }, + "node_modules/@typescript/lib-es2023": { + "name": "@better-typescript-lib/es2023", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@better-typescript-lib/es2023/-/es2023-2.9.0.tgz", + "integrity": "sha512-OhFg1gIoO1IXdcWexLiFUpNiMeI54eSxi5p/r+VQgqbXtwqa2DvC17hR0kV1w6axjUsb/PFc8rAordS23xdb2A==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "typescript": ">=4.5.2" + } + }, + "node_modules/@typescript/lib-es5": { + "name": "@better-typescript-lib/es5", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@better-typescript-lib/es5/-/es5-2.9.0.tgz", + "integrity": "sha512-jP76ajsZgAuMl1iaSGjFct70RwPeWkohAb7p6FgT9QG+KOpiXjZLLmRX2iRTnhib2pgoEvafuHjXOiqNmFYO+A==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "typescript": ">=4.5.2" + } + }, + "node_modules/@typescript/lib-es6": { + "name": "@better-typescript-lib/es6", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@better-typescript-lib/es6/-/es6-2.9.0.tgz", + "integrity": "sha512-Xczl4Z43CFkjXCEqrD2K2W1/Wh4RUYVS8IU6eC21H2zZK1G3znTPo2BG3H5N5MncyvTGdiyExRLGo7jUfWgMAA==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "typescript": ">=4.5.2" + } + }, + "node_modules/@typescript/lib-esnext": { + "name": "@better-typescript-lib/esnext", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@better-typescript-lib/esnext/-/esnext-2.9.0.tgz", + "integrity": "sha512-G2xT/xJkpXlXQK8kCBy2yueMQvFBH8fXp/pTi+snMH11NrH1tOmX9bFnskQmATE4tTzlsc6PBxY19/8oxF5UQA==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "typescript": ">=4.5.2" + } + }, + "node_modules/@typescript/lib-scripthost": { + "name": "@better-typescript-lib/scripthost", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@better-typescript-lib/scripthost/-/scripthost-2.9.0.tgz", + "integrity": "sha512-NlW35v/PbGCB6K1eqep3u5ckUbTS2LzF+tGoOxHRtgeRdLaLi0G9s9CJOT5a5Yl1X2gAogqT2KQMPyLeN4JcYw==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "typescript": ">=4.5.2" + } + }, + "node_modules/@typescript/lib-webworker": { + "name": "@better-typescript-lib/webworker", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@better-typescript-lib/webworker/-/webworker-2.9.0.tgz", + "integrity": "sha512-3jRXXzU9N8cQ6wdLKGoiCU0qBgxcmO9ALAYAe++IJqjXb1e1jaoKrA6LQO2xboy4UhbFwxyfKcr3Hl68EzTMqw==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "typescript": ">=4.5.2" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.6.tgz", + "integrity": "sha512-FxUfxaB8sCqvY46YjyAAV6c3mMIq/NWQMVvJ+uS4yxr1KzOvyg61gAuOnNvgCvO4TZ7HcLExBEsWcDu4+K4E8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.6" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.6.tgz", + "integrity": "sha512-Nsh7UW2ruK+uURIPzjJgF0YRGP5CX9nQHypA2OMqdM2FKy7rh+uv3XgPnWPw30JADbKvZ5HuBzG4gSbVDYVtiw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.6.tgz", + "integrity": "sha512-NMIrA7y5OOqddL9VtngPWYmdQU03htNKFtAYidbYfWA0TOhyGVd9tfcP4TsLWQ+RBWDZCbBqsr8xzU0ZOxYTCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.6", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.11", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.11.tgz", + "integrity": "sha512-PwAdxs7/9Hc3ieBO12tXzmTD+Ln4qhT/56S+8DvrrZ4kLDn4Z/AMUr8tXJD0axiJBS0RKIoNaR0yMuQB9v9Udg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.11", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.11", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.11.tgz", + "integrity": "sha512-pyGf8zdbDDRkBrEzf8p7BQlMKNNF5Fk/Cf/fQ6PiUz9at4OaUfyXW0dGJTo2Vl1f5U9jSLCNf0EZJEogLXoeew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.11", + "@vue/shared": "3.5.11" + } + }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/@vue/language-core": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.1.6.tgz", + "integrity": "sha512-MW569cSky9R/ooKMh6xa2g1D0AtRKbL56k83dzus/bx//RDJk24RHWkMzbAlXjMdDNyxAaagKPRquBIxkxlCkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "~2.4.1", + "@vue/compiler-dom": "^3.4.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.4.0", + "computeds": "^0.0.1", + "minimatch": "^9.0.3", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/shared": { + "version": "3.5.11", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.11.tgz", + "integrity": "sha512-W8GgysJVnFo81FthhzurdRAWP/byq3q2qIw70e0JWblzVhjgOMiC2GyovXrZTFQJnFVryYaKGP3Tc9vYzYm6PQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/better-typescript-lib": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/better-typescript-lib/-/better-typescript-lib-2.9.0.tgz", + "integrity": "sha512-wW/QvxcVu2qscos4u6Gbq61yKaAe3SuxBrPKefucxkXaBVJld5tG5Ba22btKbW+VW590UB5k7AvoAZcG+ktdZQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@typescript/lib-decorators": "npm:@better-typescript-lib/decorators@2.9.0", + "@typescript/lib-dom": "npm:@better-typescript-lib/dom@2.9.0", + "@typescript/lib-es2015": "npm:@better-typescript-lib/es2015@2.9.0", + "@typescript/lib-es2016": "npm:@better-typescript-lib/es2016@2.9.0", + "@typescript/lib-es2017": "npm:@better-typescript-lib/es2017@2.9.0", + "@typescript/lib-es2018": "npm:@better-typescript-lib/es2018@2.9.0", + "@typescript/lib-es2019": "npm:@better-typescript-lib/es2019@2.9.0", + "@typescript/lib-es2020": "npm:@better-typescript-lib/es2020@2.9.0", + "@typescript/lib-es2021": "npm:@better-typescript-lib/es2021@2.9.0", + "@typescript/lib-es2022": "npm:@better-typescript-lib/es2022@2.9.0", + "@typescript/lib-es2023": "npm:@better-typescript-lib/es2023@2.9.0", + "@typescript/lib-es5": "npm:@better-typescript-lib/es5@2.9.0", + "@typescript/lib-es6": "npm:@better-typescript-lib/es6@2.9.0", + "@typescript/lib-esnext": "npm:@better-typescript-lib/esnext@2.9.0", + "@typescript/lib-scripthost": "npm:@better-typescript-lib/scripthost@2.9.0", + "@typescript/lib-webworker": "npm:@better-typescript-lib/webworker@2.9.0" + }, + "peerDependencies": { + "typescript": ">=4.5.2" + } + }, + "node_modules/bin-check": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bin-check/-/bin-check-4.1.0.tgz", + "integrity": "sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^0.7.0", + "executable": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/bin-version": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bin-version/-/bin-version-6.0.0.tgz", + "integrity": "sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "find-versions": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bin-version-check": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bin-version-check/-/bin-version-check-5.1.0.tgz", + "integrity": "sha512-bYsvMqJ8yNGILLz1KP9zKLzQ6YpljV3ln1gqhuLkUtyfGi3qXKGuK2p+U4NAvjVFzDFiBBtOpCOSFNuYYEGZ5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bin-version": "^6.0.0", + "semver": "^7.5.3", + "semver-truncate": "^3.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bin-version/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/bin-version/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/bin-version/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bin-version/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bin-version/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bin-version/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/bin-version/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bin-version/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/bin-version/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/compare-versions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", + "dev": true, + "license": "MIT" + }, + "node_modules/computeds": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz", + "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/executable": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", + "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ext-list": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", + "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.28.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ext-name": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", + "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ext-list": "^2.0.0", + "sort-keys-length": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-type": { + "version": "17.1.6", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-17.1.6.tgz", + "integrity": "sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-web-to-node-stream": "^3.0.2", + "strtok3": "^7.0.0-alpha.9", + "token-types": "^5.0.0-alpha.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/filename-reserved-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz", + "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/filenamify": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-5.1.1.tgz", + "integrity": "sha512-M45CbrJLGACfrPOkrTp3j2EcO9OBkKUYME0eiqOCa7i2poaklU0jhlIaMlr8ijLorT0uLAzrn3qXOp5684CkfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "filename-reserved-regex": "^3.0.0", + "strip-outer": "^2.0.0", + "trim-repeated": "^2.0.0" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-versions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-5.1.0.tgz", + "integrity": "sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver-regex": "^4.0.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "license": "ISC", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.53.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz", + "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mlly": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.2.tgz", + "integrity": "sha512-tN3dvVHYVz4DhSXinXIk7u9syPYaJvio118uomkovAtWBT+RdbP6Lfh/5Lvo519YMmwBafwlh20IPTXIStscpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.12.1", + "pathe": "^1.1.2", + "pkg-types": "^1.2.0", + "ufo": "^1.5.4" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nodemon": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz", + "integrity": "sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/nodemon/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-watch": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/npm-watch/-/npm-watch-0.13.0.tgz", + "integrity": "sha512-MYcgocqCzYA44feZhFoYj69FfSaO0EeRE1gcRcmPaXIpNhUMAhNJ1pwic2C4Hn0OPOQmZKSl90CPgmwvOsVhTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "nodemon": "^3.0.1", + "through2": "^4.0.2" + }, + "bin": { + "npm-watch": "cli.js" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-filter-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-2.0.0.tgz", + "integrity": "sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "arch": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/peek-readable": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.2.0.tgz", + "integrity": "sha512-U94a+eXHzct7vAd19GH3UQ2dH4Satbng0MyYTMaQatL0pvYYL5CTPR25HBhKtecl+4bfu1/i3vC6k0hydO5Vcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/picocolors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/piscina": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.7.0.tgz", + "integrity": "sha512-b8hvkpp9zS0zsfa939b/jXbe64Z2gZv0Ha7FYPNUiDIB1y2AtxcOZdfP8xN8HFjUaqQiT9gRlfjAsoL8vdJ1Iw==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "@napi-rs/nice": "^1.0.1" + } + }, + "node_modules/pkg-types": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.0.tgz", + "integrity": "sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.7", + "mlly": "^1.7.1", + "pathe": "^1.1.2" + } + }, + "node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readable-web-to-node-stream": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", + "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", + "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.24.0", + "@rollup/rollup-android-arm64": "4.24.0", + "@rollup/rollup-darwin-arm64": "4.24.0", + "@rollup/rollup-darwin-x64": "4.24.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", + "@rollup/rollup-linux-arm-musleabihf": "4.24.0", + "@rollup/rollup-linux-arm64-gnu": "4.24.0", + "@rollup/rollup-linux-arm64-musl": "4.24.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", + "@rollup/rollup-linux-riscv64-gnu": "4.24.0", + "@rollup/rollup-linux-s390x-gnu": "4.24.0", + "@rollup/rollup-linux-x64-gnu": "4.24.0", + "@rollup/rollup-linux-x64-musl": "4.24.0", + "@rollup/rollup-win32-arm64-msvc": "4.24.0", + "@rollup/rollup-win32-ia32-msvc": "4.24.0", + "@rollup/rollup-win32-x64-msvc": "4.24.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-regex": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", + "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver-truncate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/semver-truncate/-/semver-truncate-3.0.0.tgz", + "integrity": "sha512-LJWA9kSvMolR51oDE6PN3kALBNaUdkxzAGcexw8gjMA8xr5zUqK0JiR3CgARSqanYF3Z1YHvsErb1KDgh+v7Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sort-keys-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", + "integrity": "sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "sort-keys": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-outer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-2.0.0.tgz", + "integrity": "sha512-A21Xsm1XzUkK0qK1ZrytDUvqsQWict2Cykhvi0fBQntGG5JSprESasEyV1EZ/4CiR5WB5KjzLTrP/bO37B0wPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strtok3": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.1.1.tgz", + "integrity": "sha512-mKX8HA/cdBqMKUr0MMZAFssCkIGoZeSCMXgnt79yKxNFguMLVFgRe6wB+fsL0NmoHDbeyZXczy7vEPSoo3rkzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^5.1.3" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/token-types": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", + "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/trim-repeated": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-2.0.0.tgz", + "integrity": "sha512-QUHBFTJGdOwmp0tbOG505xAgOp/YliZP/6UgafFXYZ26WT1bvQmSMJUvkeVSASuJJHbqsFbynTvkd5W8RBTipg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^5.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/typescript": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unplugin": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.14.1.tgz", + "integrity": "sha512-lBlHbfSFPToDYp9pjXlUEFVxYLaue9f9T1HC+4OHlmj+HnMDdz9oZY+erXfoCe/5V/7gKUSY2jpXPb9S7f0f/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.12.1", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "webpack-sources": "^3" + }, + "peerDependenciesMeta": { + "webpack-sources": { + "optional": true + } + } + }, + "node_modules/unplugin-swc": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/unplugin-swc/-/unplugin-swc-1.5.1.tgz", + "integrity": "sha512-/ZLrPNjChhGx3Z95pxJ4tQgfI6rWqukgYHKflrNB4zAV1izOQuDhkTn55JWeivpBxDCoK7M/TStb2aS/14PS/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0", + "load-tsconfig": "^0.2.5", + "unplugin": "^1.11.0" + }, + "peerDependencies": { + "@swc/core": "^1.2.108" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "5.4.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", + "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-plugin-dts": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/vite-plugin-dts/-/vite-plugin-dts-4.2.3.tgz", + "integrity": "sha512-O5NalzHANQRwVw1xj8KQun3Bv8OSDAlNJXrnqoAz10BOuW8FVvY5g4ygj+DlJZL5mtSPuMu9vd3OfrdW5d4k6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/api-extractor": "7.47.7", + "@rollup/pluginutils": "^5.1.0", + "@volar/typescript": "^2.4.4", + "@vue/language-core": "2.1.6", + "compare-versions": "^6.1.1", + "debug": "^4.3.6", + "kolorist": "^1.8.0", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.11" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "typescript": "*", + "vite": "*" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/threads/package.json b/threads/package.json new file mode 100644 index 0000000..7ac794d --- /dev/null +++ b/threads/package.json @@ -0,0 +1,74 @@ +{ + "name": "@oligami/browser_wasi_shim-threads", + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "npm run worker && vite build", + "prepare": "npm run build", + "fmt": "biome format --write .", + "lint": "biome lint src examples import-module-test", + "check": "biome check && tsc --noEmit", + "worker": "spack --config ./src/shared_array_buffer/worker_background/spack.config.cjs && node src/shared_array_buffer/worker_background/minify.js", + "watch": "npm-watch worker" + }, + "watch": { + "worker": "src/shared_array_buffer/worker_background/*.ts" + }, + "devDependencies": { + "@biomejs/biome": "1.9.3", + "@swc/cli": "^0.4.0", + "@swc/core": "^1.7.28", + "better-typescript-lib": "^2.8.0", + "npm-watch": "^0.13.0", + "typescript": "^5.6.2", + "unplugin-swc": "^1.5.1", + "vite": "^5.4.8", + "vite-plugin-dts": "^4.2.3" + }, + "dependencies": { + "@bjorn3/browser_wasi_shim": "^0.3.0", + "@oligami/browser_wasi_shim-threads": "file:" + }, + "peerDependencies": { + "@bjorn3/browser_wasi_shim": "^0.3.0" + }, + "private": false, + "publishConfig": { + "access": "public" + }, + "author": "oligami (https://github.com/oligamiq)", + "license": "MIT OR Apache-2.0", + "description": "A pure javascript shim for WASI with support for threads", + "homepage": "https://github.com/bjorn3/browser_wasi_shim/tree/main/threads#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/bjorn3/browser_wasi_shim.git" + }, + "bugs": { + "url": "https://github.com/bjorn3/browser_wasi_shim/issues" + }, + "main": "./dist/browser-wasi-shim-threads.es.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/browser-wasi-shim-threads.es.js" + }, + "require": { + "types": "./dist/index.d.ts", + "default": "./dist/browser-wasi-shim-threads.cjs.js" + }, + "node": { + "types": "./dist/index.d.ts", + "default": "./dist/browser-wasi-shim-threads.cjs.js" + }, + "types": "./dist/index.d.ts", + "default": "./dist/browser-wasi-shim-threads.es.js" + } + }, + "files": ["dist", "src"], + "module": "dist/browser-wasi-shim-threads.es.js", + "keywords": ["wasi", "webassembly", "threads", "worker", "webworker"] +} diff --git a/threads/src/animals.ts b/threads/src/animals.ts new file mode 100644 index 0000000..1282977 --- /dev/null +++ b/threads/src/animals.ts @@ -0,0 +1,1322 @@ +import { WASIProcExit } from "@bjorn3/browser_wasi_shim"; +import type { WASIFarmRef } from "./ref.js"; +import type { WASIFarmRefObject } from "./ref.js"; +import type { FdCloseSender } from "./sender.js"; +import { WASIFarmRefUseArrayBuffer } from "./shared_array_buffer/index.js"; +import type { WASIFarmRefUseArrayBufferObject } from "./shared_array_buffer/index.js"; +import { ThreadSpawner } from "./shared_array_buffer/index.js"; +import { wasi } from "@bjorn3/browser_wasi_shim"; + +export class WASIFarmAnimal { + args: Array; + env: Array; + + private wasi_farm_refs: WASIFarmRef[]; + + private id_in_wasi_farm_ref: Array; + + inst: { exports: { memory: WebAssembly.Memory } } | undefined; + // biome-ignore lint/suspicious/noExplicitAny: + wasiImport: { [key: string]: (...args: Array) => unknown }; + + wasiThreadImport: { + "thread-spawn": (start_arg: number) => number; + }; + + private can_array_buffer; + + private can_thread_spawn?: boolean; + + private thread_spawner?: ThreadSpawner; + + async wait_worker_background_worker(): Promise { + await this.thread_spawner?.wait_worker_background_worker(); + } + + check_worker_background_worker(): void { + this.thread_spawner?.check_worker_background_worker(); + } + + // Each process has a specific fd that it can access. + // If it does not exist in the map, it cannot be accessed. + // child process can access parent process's fd. + // so, it is necessary to manage the fd on global scope. + // [fd, wasi_ref_n] + protected fd_map: Array<[number, number]> = []; + + protected get_fd_and_wasi_ref( + fd: number, + ): [number | undefined, WASIFarmRef | undefined] { + const mapped_fd_and_wasi_ref_n = this.fd_map[fd]; + if (!mapped_fd_and_wasi_ref_n) { + // console.log("fd", fd, "is not found"); + return [undefined, undefined]; + } + const [mapped_fd, wasi_ref_n] = mapped_fd_and_wasi_ref_n; + // console.log("fd", fd, "is found", "mapped_fd", mapped_fd, "wasi_ref_n", wasi_ref_n); + return [mapped_fd, this.wasi_farm_refs[wasi_ref_n]]; + } + + protected get_fd_and_wasi_ref_n( + fd: number, + ): [number | undefined, number | undefined] { + const mapped_fd_and_wasi_ref_n = this.fd_map[fd]; + if (!mapped_fd_and_wasi_ref_n) { + // console.log("fd", fd, "is not found"); + return [undefined, undefined]; + } + const [mapped_fd, wasi_ref_n] = mapped_fd_and_wasi_ref_n; + // console.log("fd", fd, "is found", "mapped_fd", mapped_fd, "wasi_ref_n", wasi_ref_n); + return [mapped_fd, wasi_ref_n]; + } + + /// Start a WASI command + start(instance: { + // FIXME v0.3: close opened Fds after execution + exports: { memory: WebAssembly.Memory; _start: () => unknown }; + }) { + this.inst = instance; + + try { + instance.exports._start(); + + if (this.can_thread_spawn) { + if (!this.thread_spawner) { + throw new Error("thread_spawner is not defined"); + } + this.thread_spawner.done_notify(0); + } + + return 0; + } catch (e) { + if (e instanceof WASIProcExit) { + if (this.can_thread_spawn) { + // biome-ignore lint/style/noNonNullAssertion: + this.thread_spawner!.done_notify(e.code); + } + + return e.code; + } + throw e; + } + } + + /// Start a WASI command on a thread + /// If a module has child threads and a child thread throws an error, + /// the main thread should also be stopped, + /// but there is no way to stop it, + /// so the entire worker is stopped. + /// If it is not necessary, do not use it. + /// Custom imports are not implemented, + /// function because it cannot be passed to other threads. + /// If the sharedObject library someday supports synchronization, it could be used to support this. + async async_start_on_thread(): Promise { + if (!this.can_thread_spawn || !this.thread_spawner) { + throw new Error("thread_spawn is not supported"); + } + + await this.wait_worker_background_worker(); + + if (this.inst) { + throw new Error("what happened?"); + } + + const view = new Uint8Array(this.get_share_memory().buffer); + view.fill(0); + + await this.thread_spawner.async_start_on_thread( + this.args, + this.env, + this.fd_map, + ); + + const code = await this.thread_spawner.async_wait_done_or_error(); + + return code; + } + + block_start_on_thread(): number { + if (!this.can_thread_spawn || !this.thread_spawner) { + throw new Error("thread_spawn is not supported"); + } + + console.log("block_start_on_thread"); + + this.check_worker_background_worker(); + + console.log("block_start_on_thread"); + + if (this.inst) { + throw new Error("what happened?"); + } + + const view = new Uint8Array(this.get_share_memory().buffer); + view.fill(0); + + console.log("block_start_on_thread: start"); + + this.thread_spawner.block_start_on_thread(this.args, this.env, this.fd_map); + + console.log("block_start_on_thread: wait"); + + const code = this.thread_spawner.block_wait_done_or_error(); + + console.log("block_start_on_thread: done"); + + return code; + } + + wasi_thread_start( + instance: { + exports: { + memory: WebAssembly.Memory; + wasi_thread_start: (thread_id: number, start_arg: number) => void; + }; + }, + thread_id: number, + start_arg: number, + ) { + this.inst = instance; + try { + instance.exports.wasi_thread_start(thread_id, start_arg); + return 0; + } catch (e) { + if (e instanceof WASIProcExit) { + return e.code; + } + throw e; + } + } + + /// Initialize a WASI reactor + initialize(instance: { + exports: { memory: WebAssembly.Memory; _initialize?: () => unknown }; + }) { + this.inst = instance; + if (instance.exports._initialize) { + instance.exports._initialize(); + } + } + + private mapping_fds( + wasi_farm_refs: Array, + override_fd_maps?: Array, + ) { + this.fd_map = [undefined, undefined, undefined] as unknown as Array< + [number, number] + >; + + // console.log("wasi_farm_refs", wasi_farm_refs); + for (let i = 0; i < wasi_farm_refs.length; i++) { + // console.log("fd_map", [...fd_map]); + + const wasi_farm_ref = wasi_farm_refs[i]; + // console.log("override_fd_map", wasi_farm_ref.default_fds); + const override_fd_map = override_fd_maps + ? override_fd_maps[i] + : wasi_farm_ref.default_fds; + // console.log("override_fd_map", override_fd_map); + const stdin = wasi_farm_ref.get_stdin(); + const stdout = wasi_farm_ref.get_stdout(); + const stderr = wasi_farm_ref.get_stderr(); + // console.log("stdin", stdin, "stdout", stdout, "stderr", stderr); + if (stdin !== undefined) { + if (this.fd_map[0] === undefined) { + if (override_fd_map.includes(stdin)) { + this.fd_map[0] = [stdin, i]; + } + } + } + if (stdout !== undefined) { + // console.log("stdout", stdout, i, "override_fd_map", override_fd_map); + if (this.fd_map[1] === undefined) { + if (override_fd_map.includes(stdout)) { + // console.log("stdout defined"); + this.fd_map[1] = [stdout, i]; + } + } + } + if (stderr !== undefined) { + if (this.fd_map[2] === undefined) { + if (override_fd_map.includes(stderr)) { + this.fd_map[2] = [stderr, i]; + } + } + } + for (const j of override_fd_map) { + if (j === stdin || j === stdout || j === stderr) { + continue; + } + this.map_new_fd(j, i); + } + wasi_farm_ref.set_park_fds_map(override_fd_map); + + // console.log("this.fd_map", this.fd_map); + } + + if (this.fd_map[0] === undefined) { + throw new Error("stdin is not found"); + } + if (this.fd_map[1] === undefined) { + throw new Error("stdout is not found"); + } + if (this.fd_map[2] === undefined) { + throw new Error("stderr is not found"); + } + } + + private map_new_fd(fd: number, wasi_ref_n: number): number { + let n = -1; + // 0, 1, 2 are reserved for stdin, stdout, stderr + for (let i = 3; i < this.fd_map.length; i++) { + if (this.fd_map[i] === undefined) { + n = i; + break; + } + } + if (n === -1) { + n = + (this.fd_map as Array<[number, number] | undefined>).push(undefined) - + 1; + } + this.fd_map[n] = [fd, wasi_ref_n]; + return n; + } + + private map_new_fd_and_notify(fd: number, wasi_ref_n: number): number { + const n = this.map_new_fd(fd, wasi_ref_n); + // console.log("animals: fd", fd, "is mapped to", n); + // console.log("wasi_ref_n", wasi_ref_n); + this.wasi_farm_refs[wasi_ref_n].set_park_fds_map([fd]); + return n; + } + + // @ts-ignore + private map_set_fd_and_notify(fd: number, wasi_ref_n: number, index: number) { + if (this.fd_map[index] !== undefined) { + throw new Error("fd is already mapped"); + } + this.fd_map[index] = [fd, wasi_ref_n]; + this.wasi_farm_refs[wasi_ref_n].set_park_fds_map([fd]); + } + + private check_fds() { + const rm_fds: Array<[number, number]> = []; + for (let i = 0; i < this.id_in_wasi_farm_ref.length; i++) { + const id = this.id_in_wasi_farm_ref[i]; + const removed_fds = (this.wasi_farm_refs[i] as FdCloseSender).get(id); + if (removed_fds) { + for (const fd of removed_fds) { + rm_fds.push([fd, i]); + } + } + } + + if (rm_fds.length > 0) { + for (let i = 0; i < this.fd_map.length; i++) { + const fd_and_wasi_ref_n = this.fd_map[i]; + if (!fd_and_wasi_ref_n) { + continue; + } + const [fd, wasi_ref_n] = fd_and_wasi_ref_n; + for (const [rm_fd_fd, rm_fd_wasi_ref_n] of rm_fds) { + if (fd === rm_fd_fd && wasi_ref_n === rm_fd_wasi_ref_n) { + (this.fd_map as Array<[number, number] | undefined>)[i] = undefined; + // console.log("fd", i, "is removed"); + break; + } + } + // console.log("fd_and_wasi_ref_n", fd_and_wasi_ref_n); + } + // console.log("rm_fds.length", rm_fds.length); + // console.log("rm_fds", rm_fds); + } + } + + get_share_memory(): WebAssembly.Memory { + if (!this.thread_spawner) { + throw new Error("thread_spawner is not defined"); + } + + return this.thread_spawner.get_share_memory(); + } + + constructor( + wasi_farm_refs: WASIFarmRefObject[] | WASIFarmRefObject, + args: Array, + env: Array, + options: { + can_thread_spawn?: boolean; + thread_spawn_worker_url?: string; + thread_spawn_wasm?: WebAssembly.Module; + hand_override_fd_map?: Array<[number, number]>; + } = {}, + override_fd_maps?: Array, + thread_spawner?: ThreadSpawner, + ) { + let wasi_farm_refs_tmp: WASIFarmRefObject[]; + if (Array.isArray(wasi_farm_refs)) { + wasi_farm_refs_tmp = + wasi_farm_refs as unknown as Array; + } else { + wasi_farm_refs_tmp = [wasi_farm_refs as unknown as WASIFarmRefObject]; + } + + try { + new SharedArrayBuffer(4); + this.can_array_buffer = true; + } catch (_) { + this.can_array_buffer = false; + } + + this.id_in_wasi_farm_ref = []; + this.wasi_farm_refs = []; + for (let i = 0; i < wasi_farm_refs_tmp.length; i++) { + if (this.can_array_buffer) { + this.wasi_farm_refs.push( + WASIFarmRefUseArrayBuffer.init_self( + wasi_farm_refs_tmp[i] as WASIFarmRefUseArrayBufferObject, + ), + ); + } else { + throw new Error("Non SharedArrayBuffer is not supported yet"); + } + this.id_in_wasi_farm_ref.push(this.wasi_farm_refs[i].set_id()); + } + + // console.log("this.wasi_farm_refs", this.wasi_farm_refs); + + if (options.can_thread_spawn) { + this.can_thread_spawn = options.can_thread_spawn; + + if (thread_spawner) { + if (!(thread_spawner instanceof ThreadSpawner)) { + throw new Error("thread_spawner is not ThreadSpawner"); + } + + this.thread_spawner = thread_spawner; + } else { + if (options.thread_spawn_worker_url === undefined) { + throw new Error("thread_spawn_worker_url is not defined"); + } + if (options.thread_spawn_wasm === undefined) { + throw new Error("thread_spawn_wasm is not defined"); + } + + this.thread_spawner = new ThreadSpawner( + options.thread_spawn_worker_url, + wasi_farm_refs_tmp, + undefined, + undefined, + undefined, + options.thread_spawn_wasm, + ); + } + } + + this.mapping_fds(this.wasi_farm_refs, override_fd_maps); + + if (options.hand_override_fd_map) { + this.fd_map = options.hand_override_fd_map; + } + + // console.log("this.fd_map", this.fd_map); + + this.args = args; + this.env = env; + const self = this; + this.wasiImport = { + args_sizes_get(argc: number, argv_buf_size: number): number { + self.check_fds(); + // biome-ignore lint/style/noNonNullAssertion: + const buffer = new DataView(self.inst!.exports.memory.buffer); + buffer.setUint32(argc, self.args.length, true); + let buf_size = 0; + for (const arg of self.args) { + buf_size += arg.length + 1; + } + buffer.setUint32(argv_buf_size, buf_size, true); + return 0; + }, + args_get(argv: number, argv_buf: number): number { + self.check_fds(); + // biome-ignore lint/style/noNonNullAssertion: + const buffer = new DataView(self.inst!.exports.memory.buffer); + // biome-ignore lint/style/noNonNullAssertion: + const buffer8 = new Uint8Array(self.inst!.exports.memory.buffer); + for (let i = 0; i < self.args.length; i++) { + buffer.setUint32(argv, argv_buf, true); + // biome-ignore lint/style/noParameterAssign: + argv += 4; + const arg = new TextEncoder().encode(self.args[i]); + buffer8.set(arg, argv_buf); + buffer.setUint8(argv_buf + arg.length, 0); + // biome-ignore lint/style/noParameterAssign: + argv_buf += arg.length + 1; + } + return 0; + }, + environ_sizes_get(environ_count: number, environ_size: number): number { + self.check_fds(); + // biome-ignore lint/style/noNonNullAssertion: + const buffer = new DataView(self.inst!.exports.memory.buffer); + buffer.setUint32(environ_count, self.env.length, true); + let buf_size = 0; + for (const environ of self.env) { + buf_size += environ.length + 1; + } + buffer.setUint32(environ_size, buf_size, true); + return 0; + }, + environ_get(environ: number, environ_buf: number): number { + self.check_fds(); + // biome-ignore lint/style/noNonNullAssertion: + const buffer = new DataView(self.inst!.exports.memory.buffer); + // biome-ignore lint/style/noNonNullAssertion: + const buffer8 = new Uint8Array(self.inst!.exports.memory.buffer); + for (let i = 0; i < self.env.length; i++) { + buffer.setUint32(environ, environ_buf, true); + // biome-ignore lint/style/noParameterAssign: + environ += 4; + const e = new TextEncoder().encode(self.env[i]); + buffer8.set(e, environ_buf); + buffer.setUint8(environ_buf + e.length, 0); + // biome-ignore lint/style/noParameterAssign: + environ_buf += e.length + 1; + } + return 0; + }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + clock_res_get(id: number, res_ptr: number): number { + self.check_fds(); + let resolutionValue: bigint; + switch (id) { + case wasi.CLOCKID_MONOTONIC: { + // https://developer.mozilla.org/en-US/docs/Web/API/Performance/now + // > Resolution in isolated contexts: 5 microseconds + resolutionValue = 5_000n; // 5 microseconds + break; + } + case wasi.CLOCKID_REALTIME: { + resolutionValue = 1_000_000n; // 1 millisecond? + break; + } + default: + return wasi.ERRNO_NOSYS; + } + // biome-ignore lint/style/noNonNullAssertion: + const view = new DataView(self.inst!.exports.memory.buffer); + view.setBigUint64(res_ptr, resolutionValue, true); + return wasi.ERRNO_SUCCESS; + }, + clock_time_get(id: number, _precision: bigint, time: number): number { + self.check_fds(); + // biome-ignore lint/style/noNonNullAssertion: + const buffer = new DataView(self.inst!.exports.memory.buffer); + if (id === wasi.CLOCKID_REALTIME) { + buffer.setBigUint64( + time, + BigInt(new Date().getTime()) * 1_000_000n, + true, + ); + } else if (id === wasi.CLOCKID_MONOTONIC) { + let monotonic_time: bigint; + try { + monotonic_time = BigInt(Math.round(performance.now() * 1000000)); + } catch (e) { + // performance.now() is only available in browsers. + // TODO use the perf_hooks builtin module for NodeJS + monotonic_time = 0n; + } + buffer.setBigUint64(time, monotonic_time, true); + } else { + // TODO + buffer.setBigUint64(time, 0n, true); + } + return 0; + }, + fd_advise( + fd: number, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _offset: bigint, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _len: bigint, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _advice: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + return wasi_farm_ref.fd_advise(mapped_fd); + }, + fd_allocate(fd: number, offset: bigint, len: bigint) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + return wasi_farm_ref.fd_allocate(mapped_fd, offset, len); + }, + fd_close(fd: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + const ret = wasi_farm_ref.fd_close(mapped_fd); + self.check_fds(); + return ret; + }, + fd_datasync(fd: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + return wasi_farm_ref.fd_datasync(mapped_fd); + }, + fd_fdstat_get(fd: number, fdstat_ptr: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + const [fdstat, ret] = wasi_farm_ref.fd_fdstat_get(mapped_fd); + if (fdstat) { + fdstat.write_bytes( + // biome-ignore lint/style/noNonNullAssertion: + new DataView(self.inst!.exports.memory.buffer), + fdstat_ptr, + ); + } + return ret; + }, + fd_fdstat_set_flags(fd: number, flags: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + return wasi_farm_ref.fd_fdstat_set_flags(mapped_fd, flags); + }, + fd_fdstat_set_rights( + fd: number, + fs_rights_base: bigint, + fs_rights_inheriting: bigint, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + return wasi_farm_ref.fd_fdstat_set_rights( + mapped_fd, + fs_rights_base, + fs_rights_inheriting, + ); + }, + fd_filestat_get(fd: number, filestat_ptr: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + const [filestat, ret] = wasi_farm_ref.fd_filestat_get(mapped_fd); + if (filestat) { + filestat.write_bytes( + // biome-ignore lint/style/noNonNullAssertion: + new DataView(self.inst!.exports.memory.buffer), + filestat_ptr, + ); + } + return ret; + }, + fd_filestat_set_size(fd: number, size: bigint) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + return wasi_farm_ref.fd_filestat_set_size(mapped_fd, size); + }, + fd_filestat_set_times( + fd: number, + atim: bigint, + mtim: bigint, + fst_flags: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + return wasi_farm_ref.fd_filestat_set_times( + mapped_fd, + atim, + mtim, + fst_flags, + ); + }, + fd_pread( + fd: number, + iovs_ptr: number, + iovs_len: number, + offset: bigint, + nread_ptr: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + // biome-ignore lint/style/noNonNullAssertion: + const buffer = new DataView(self.inst!.exports.memory.buffer); + // biome-ignore lint/style/noNonNullAssertion: + const buffer8 = new Uint8Array(self.inst!.exports.memory.buffer); + const iovs_view = new Uint32Array( + buffer.buffer, + iovs_ptr, + iovs_len * 2, + ); + const [nerad_and_read_data, ret] = wasi_farm_ref.fd_pread( + mapped_fd, + iovs_view, + offset, + ); + if (nerad_and_read_data) { + const iovecs = wasi.Iovec.read_bytes_array( + buffer, + iovs_ptr, + iovs_len, + ); + const [nread, read_data] = nerad_and_read_data; + buffer.setUint32(nread_ptr, nread, true); + let nreaded = 0; + for (const iovec of iovecs) { + if (nreaded + iovec.buf_len >= read_data.length) { + buffer8.set(read_data, iovec.buf); + break; + } + buffer8.set( + read_data.slice(nreaded, nreaded + iovec.buf_len), + iovec.buf, + ); + nreaded += iovec.buf_len; + } + } + return ret; + }, + fd_prestat_get(fd: number, prestat_ptr: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + const [prestat, ret] = wasi_farm_ref.fd_prestat_get(mapped_fd); + if (prestat) { + const [tag, name_len] = prestat; + // biome-ignore lint/style/noNonNullAssertion: + const buffer = new DataView(self.inst!.exports.memory.buffer); + buffer.setUint32(prestat_ptr, tag, true); + buffer.setUint32(prestat_ptr + 4, name_len, true); + } + return ret; + }, + fd_prestat_dir_name(fd: number, path_ptr: number, path_len: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return [undefined, wasi.ERRNO_BADF]; + } + // console.log("fd_prestat_dir_name: fd", mapped_fd, "path_len", path_len); + const [path, ret] = wasi_farm_ref.fd_prestat_dir_name( + mapped_fd, + path_len, + ); + if (path) { + // console.log("fd_prestat_dir_name", new TextDecoder().decode(path)); + // console.log("fd_prestat_dir_name", path); + // biome-ignore lint/style/noNonNullAssertion: + const buffer8 = new Uint8Array(self.inst!.exports.memory.buffer); + buffer8.set(path, path_ptr); + } + return ret; + }, + fd_pwrite( + fd: number, + iovs_ptr: number, + iovs_len: number, + offset: bigint, + nwritten_ptr: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + // biome-ignore lint/style/noNonNullAssertion: + const buffer = new DataView(self.inst!.exports.memory.buffer); + // biome-ignore lint/style/noNonNullAssertion: + const buffer8 = new Uint8Array(self.inst!.exports.memory.buffer); + const iovecs = wasi.Ciovec.read_bytes_array(buffer, iovs_ptr, iovs_len); + const data = new Uint8Array( + iovecs.reduce((acc, iovec) => acc + iovec.buf_len, 0), + ); + let nwritten = 0; + for (const iovec of iovecs) { + data.set( + buffer8.slice(iovec.buf, iovec.buf + iovec.buf_len), + nwritten, + ); + nwritten += iovec.buf_len; + } + const [written, ret] = wasi_farm_ref.fd_pwrite(mapped_fd, data, offset); + if (written) { + buffer.setUint32(nwritten_ptr, written, true); + } + return ret; + }, + fd_read( + fd: number, + iovs_ptr: number, + iovs_len: number, + nread_ptr: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + // biome-ignore lint/style/noNonNullAssertion: + const buffer = new DataView(self.inst!.exports.memory.buffer); + // biome-ignore lint/style/noNonNullAssertion: + const buffer8 = new Uint8Array(self.inst!.exports.memory.buffer); + const iovs_view = new Uint32Array( + buffer.buffer, + iovs_ptr, + iovs_len * 2, + ); + + const [nerad_and_read_data, ret] = wasi_farm_ref.fd_read( + mapped_fd, + iovs_view, + ); + if (nerad_and_read_data) { + const iovecs = wasi.Iovec.read_bytes_array( + buffer, + iovs_ptr, + iovs_len, + ); + const [nread, read_data] = nerad_and_read_data; + + // console.log("fd_read: nread", nread, new TextDecoder().decode(read_data)); + + // fd_read: ref: 14 30 14 + // animals.ts:325 fd_read: nread 14 Hello, world! + + buffer.setUint32(nread_ptr, nread, true); + let nreaded = 0; + for (const iovec of iovecs) { + if (nreaded + iovec.buf_len >= read_data.length) { + buffer8.set(read_data, iovec.buf); + break; + } + buffer8.set( + read_data.slice(nreaded, nreaded + iovec.buf_len), + iovec.buf, + ); + nreaded += iovec.buf_len; + } + } + return ret; + }, + fd_readdir( + fd: number, + buf_ptr: number, + buf_len: number, + cookie: bigint, + buf_used_ptr: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return [undefined, wasi.ERRNO_BADF]; + } + // biome-ignore lint/style/noNonNullAssertion: + const buffer = new DataView(self.inst!.exports.memory.buffer); + // biome-ignore lint/style/noNonNullAssertion: + const buffer8 = new Uint8Array(self.inst!.exports.memory.buffer); + const [nerad_and_read_data, ret] = wasi_farm_ref.fd_readdir( + mapped_fd, + buf_len, + cookie, + ); + if (nerad_and_read_data) { + const [read_data, buf_used] = nerad_and_read_data; + buffer.setUint32(buf_used_ptr, buf_used, true); + buffer8.set(read_data, buf_ptr); + } + return ret; + }, + fd_renumber(fd: number, to: number) { + self.check_fds(); + + const [mapped_to, wasi_farm_ref_to] = self.get_fd_and_wasi_ref(to); + + if (mapped_to !== undefined && wasi_farm_ref_to !== undefined) { + const ret = wasi_farm_ref_to.fd_close(mapped_to); + self.check_fds(); + if (ret !== wasi.ERRNO_SUCCESS) { + return ret; + } + } + + if (self.fd_map[to]) { + throw new Error("fd is already mapped"); + } + + self.fd_map[to] = self.fd_map[fd]; + + (self.fd_map as Array<[number, number] | undefined>)[fd] = undefined; + + return wasi.ERRNO_SUCCESS; + }, + fd_seek( + fd: number, + offset: bigint, + whence: number, + newoffset_ptr: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return [undefined, wasi.ERRNO_BADF]; + } + const [newoffset, ret] = wasi_farm_ref.fd_seek( + mapped_fd, + offset, + whence, + ); + if (newoffset) { + // biome-ignore lint/style/noNonNullAssertion: + const buffer = new DataView(self.inst!.exports.memory.buffer); + + // wasi.ts use BigInt for offset, but API use Uint64 + buffer.setBigUint64(newoffset_ptr, newoffset, true); + } + return ret; + }, + fd_sync(fd: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + return wasi_farm_ref.fd_sync(mapped_fd); + }, + fd_tell(fd: number, newoffset_ptr: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return [undefined, wasi.ERRNO_BADF]; + } + const [newoffset, ret] = wasi_farm_ref.fd_tell(mapped_fd); + if (newoffset) { + // biome-ignore lint/style/noNonNullAssertion: + const buffer = new DataView(self.inst!.exports.memory.buffer); + buffer.setBigUint64(newoffset_ptr, newoffset, true); + } + return ret; + }, + fd_write( + fd: number, + iovs_ptr: number, + iovs_len: number, + nwritten_ptr: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + + // console.log("fd_write", fd, iovs_ptr, iovs_len, nwritten_ptr); + + // biome-ignore lint/style/noNonNullAssertion: + const buffer = new DataView(self.inst!.exports.memory.buffer); + // biome-ignore lint/style/noNonNullAssertion: + const buffer8 = new Uint8Array(self.inst!.exports.memory.buffer); + const iovecs = wasi.Ciovec.read_bytes_array(buffer, iovs_ptr, iovs_len); + // console.log("iovecs", iovecs); + const data = new Uint8Array( + iovecs.reduce((acc, iovec) => acc + iovec.buf_len, 0), + ); + // console.log("data", data); + let nwritten = 0; + for (const iovec of iovecs) { + data.set( + buffer8.slice(iovec.buf, iovec.buf + iovec.buf_len), + nwritten, + ); + nwritten += iovec.buf_len; + } + + // console.log("fd_write: ", fd, new TextDecoder().decode(data)); + + const [written, ret] = wasi_farm_ref.fd_write(mapped_fd, data); + + // console.log("fd_write end", fd, ret, written); + + if (written) { + buffer.setUint32(nwritten_ptr, written, true); + } + return ret; + }, + path_create_directory(fd: number, path_ptr: number, path_len: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + // biome-ignore lint/style/noNonNullAssertion: + const buffer8 = new Uint8Array(self.inst!.exports.memory.buffer); + const path = buffer8.slice(path_ptr, path_ptr + path_len); + return wasi_farm_ref.path_create_directory(mapped_fd, path); + }, + path_filestat_get( + fd: number, + flags: number, + path_ptr: number, + path_len: number, + filestat_ptr: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return [undefined, wasi.ERRNO_BADF]; + } + // biome-ignore lint/style/noNonNullAssertion: + const buffer = new DataView(self.inst!.exports.memory.buffer); + // biome-ignore lint/style/noNonNullAssertion: + const buffer8 = new Uint8Array(self.inst!.exports.memory.buffer); + const path = buffer8.slice(path_ptr, path_ptr + path_len); + const [filestat, ret] = wasi_farm_ref.path_filestat_get( + mapped_fd, + flags, + path, + ); + if (filestat) { + filestat.write_bytes(buffer, filestat_ptr); + } + return ret; + }, + path_filestat_set_times( + fd: number, + flags: number, + path_ptr: number, + path_len: number, + atim: bigint, + mtim: bigint, + fst_flags: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return [undefined, wasi.ERRNO_BADF]; + } + // biome-ignore lint/style/noNonNullAssertion: + const buffer8 = new Uint8Array(self.inst!.exports.memory.buffer); + const path = buffer8.slice(path_ptr, path_ptr + path_len); + return wasi_farm_ref.path_filestat_set_times( + mapped_fd, + flags, + path, + atim, + mtim, + fst_flags, + ); + }, + // TODO! Make it work with different wasi_farm_ref + path_link( + old_fd: number, + old_flags: number, + old_path_ptr: number, + old_path_len: number, + new_fd: number, + new_path_ptr: number, + new_path_len: number, + ) { + self.check_fds(); + const [mapped_old_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(old_fd); + const [mapped_new_fd, wasi_farm_ref_new] = + self.get_fd_and_wasi_ref(new_fd); + if ( + mapped_old_fd === undefined || + wasi_farm_ref === undefined || + mapped_new_fd === undefined || + wasi_farm_ref_new === undefined + ) { + return wasi.ERRNO_BADF; + } + if (wasi_farm_ref !== wasi_farm_ref_new) { + return wasi.ERRNO_BADF; + } + // biome-ignore lint/style/noNonNullAssertion: + const buffer8 = new Uint8Array(self.inst!.exports.memory.buffer); + const old_path = buffer8.slice( + old_path_ptr, + old_path_ptr + old_path_len, + ); + const new_path = buffer8.slice( + new_path_ptr, + new_path_ptr + new_path_len, + ); + return wasi_farm_ref.path_link( + mapped_old_fd, + old_flags, + old_path, + mapped_new_fd, + new_path, + ); + }, + path_open( + fd: number, + dirflags: number, + path_ptr: number, + path_len: number, + oflags: number, + fs_rights_base: bigint, + fs_rights_inheriting: bigint, + fs_flags: number, + opened_fd_ptr: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref_n] = self.get_fd_and_wasi_ref_n(fd); + if (mapped_fd === undefined || wasi_farm_ref_n === undefined) { + return wasi.ERRNO_BADF; + } + const wasi_farm_ref = self.wasi_farm_refs[wasi_farm_ref_n]; + // biome-ignore lint/style/noNonNullAssertion: + const buffer8 = new Uint8Array(self.inst!.exports.memory.buffer); + const path = buffer8.slice(path_ptr, path_ptr + path_len); + const [opened_fd, ret] = wasi_farm_ref.path_open( + mapped_fd, + dirflags, + path, + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + ); + if (opened_fd) { + if (self.fd_map.includes([opened_fd, wasi_farm_ref_n])) { + throw new Error("opened_fd already exists"); + } + const mapped_opened_fd = self.map_new_fd_and_notify( + opened_fd, + wasi_farm_ref_n, + ); + // biome-ignore lint/style/noNonNullAssertion: + const buffer = new DataView(self.inst!.exports.memory.buffer); + buffer.setUint32(opened_fd_ptr, mapped_opened_fd, true); + } + return ret; + }, + path_readlink( + fd: number, + path_ptr: number, + path_len: number, + buf_ptr: number, + buf_len: number, + buf_used_ptr: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return [undefined, wasi.ERRNO_BADF]; + } + // biome-ignore lint/style/noNonNullAssertion: + const buffer8 = new Uint8Array(self.inst!.exports.memory.buffer); + const path = buffer8.slice(path_ptr, path_ptr + path_len); + const [buf, ret] = wasi_farm_ref.path_readlink( + mapped_fd, + path, + buf_len, + ); + if (buf) { + // biome-ignore lint/style/noNonNullAssertion: + const buffer = new DataView(self.inst!.exports.memory.buffer); + buffer.setUint32(buf_used_ptr, buf.length, true); + buffer8.set(buf, buf_ptr); + } + return ret; + }, + path_remove_directory(fd: number, path_ptr: number, path_len: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + // biome-ignore lint/style/noNonNullAssertion: + const buffer8 = new Uint8Array(self.inst!.exports.memory.buffer); + const path = buffer8.slice(path_ptr, path_ptr + path_len); + return wasi_farm_ref.path_remove_directory(mapped_fd, path); + }, + // TODO! Make it work with different wasi_farm_ref + path_rename( + old_fd: number, + old_path_ptr: number, + old_path_len: number, + new_fd: number, + new_path_ptr: number, + new_path_len: number, + ) { + if (old_fd === new_fd) { + return wasi.ERRNO_SUCCESS; + } + self.check_fds(); + const [mapped_old_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(old_fd); + const [mapped_new_fd, wasi_farm_ref_new] = + self.get_fd_and_wasi_ref(new_fd); + if ( + mapped_old_fd === undefined || + wasi_farm_ref === undefined || + mapped_new_fd === undefined || + wasi_farm_ref_new === undefined + ) { + return wasi.ERRNO_BADF; + } + if (wasi_farm_ref !== wasi_farm_ref_new) { + return wasi.ERRNO_BADF; + } + // biome-ignore lint/style/noNonNullAssertion: + const buffer8 = new Uint8Array(self.inst!.exports.memory.buffer); + const old_path = buffer8.slice( + old_path_ptr, + old_path_ptr + old_path_len, + ); + const new_path = buffer8.slice( + new_path_ptr, + new_path_ptr + new_path_len, + ); + return wasi_farm_ref.path_rename( + mapped_old_fd, + old_path, + mapped_new_fd, + new_path, + ); + }, + path_symlink( + old_path_ptr: number, + old_path_len: number, + fd: number, + new_path_ptr: number, + new_path_len: number, + ) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + // biome-ignore lint/style/noNonNullAssertion: + const buffer8 = new Uint8Array(self.inst!.exports.memory.buffer); + const old_path = buffer8.slice( + old_path_ptr, + old_path_ptr + old_path_len, + ); + const new_path = buffer8.slice( + new_path_ptr, + new_path_ptr + new_path_len, + ); + return wasi_farm_ref.path_symlink(old_path, mapped_fd, new_path); + }, + path_unlink_file(fd: number, path_ptr: number, path_len: number) { + self.check_fds(); + const [mapped_fd, wasi_farm_ref] = self.get_fd_and_wasi_ref(fd); + if (mapped_fd === undefined || wasi_farm_ref === undefined) { + return wasi.ERRNO_BADF; + } + // biome-ignore lint/style/noNonNullAssertion: + const buffer8 = new Uint8Array(self.inst!.exports.memory.buffer); + const path = buffer8.slice(path_ptr, path_ptr + path_len); + return wasi_farm_ref.path_unlink_file(mapped_fd, path); + }, + poll_oneoff(_in_, _out, _nsubscriptions) { + self.check_fds(); + throw "async io not supported"; + }, + proc_exit(exit_code: number) { + self.check_fds(); + throw new WASIProcExit(exit_code); + }, + proc_raise(sig: number) { + self.check_fds(); + throw `raised signal ${sig}`; + }, + sched_yield() { + self.check_fds(); + }, + random_get(buf: number, buf_len: number) { + self.check_fds(); + const buffer8 = new Uint8Array( + // biome-ignore lint/style/noNonNullAssertion: + self.inst!.exports.memory.buffer, + ).subarray(buf, buf + buf_len); + + if ( + "crypto" in globalThis && + // biome-ignore lint/style/noNonNullAssertion: + !(self.inst!.exports.memory.buffer instanceof SharedArrayBuffer) + ) { + for (let i = 0; i < buf_len; i += 65536) { + crypto.getRandomValues(buffer8.subarray(i, i + 65536)); + } + } else { + for (let i = 0; i < buf_len; i++) { + buffer8[i] = (Math.random() * 256) | 0; + } + } + }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + sock_recv(_fd: number, _ri_data, _ri_flags) { + self.check_fds(); + throw "sockets not supported"; + }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + sock_send(_fd: number, _si_data, _si_flags) { + self.check_fds(); + throw "sockets not supported"; + }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + sock_shutdown(_fd: number, _how) { + self.check_fds(); + throw "sockets not supported"; + }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + sock_accept(_fd: number, _flags) { + self.check_fds(); + throw "sockets not supported"; + }, + }; + + this.wasiThreadImport = { + "thread-spawn": (start_arg: number) => { + self.check_fds(); + if (!self.can_thread_spawn || !self.thread_spawner) { + throw new Error("thread_spawn is not allowed"); + } + + const thread_id = self.thread_spawner.thread_spawn( + start_arg, + self.args, + self.env, + self.fd_map, + ); + + return thread_id; + }, + }; + } +} diff --git a/threads/src/farm.ts b/threads/src/farm.ts new file mode 100644 index 0000000..f2455dc --- /dev/null +++ b/threads/src/farm.ts @@ -0,0 +1,109 @@ +import type { Fd } from "@bjorn3/browser_wasi_shim"; +import type { WASIFarmPark } from "./park.js"; +import type { WASIFarmRefObject } from "./ref.js"; +import { WASIFarmParkUseArrayBuffer } from "./shared_array_buffer/index.js"; + +export class WASIFarm { + private fds: Array; + private park: WASIFarmPark; + + private can_array_buffer: boolean; + + constructor( + stdin?: Fd, + stdout?: Fd, + stderr?: Fd, + fds: Array = [], + options: { + allocator_size?: number; + } = {}, + ) { + const new_fds = []; + let stdin_ = undefined; + let stdout_ = undefined; + let stderr_ = undefined; + if (stdin) { + new_fds.push(stdin); + stdin_ = new_fds.length - 1; + } + if (stdout) { + new_fds.push(stdout); + stdout_ = new_fds.length - 1; + } + if (stderr) { + new_fds.push(stderr); + stderr_ = new_fds.length - 1; + } + new_fds.push(...fds); + + const default_allow_fds = []; + for (let i = 0; i < new_fds.length; i++) { + default_allow_fds.push(i); + } + + this.fds = new_fds; + + // WebAssembly.Memory can be used to create a SharedArrayBuffer, but it cannot be transferred by postMessage. + // Uncaught (in promise) DataCloneError: + // Failed to execute 'postMessage' on 'Worker': + // SharedArrayBuffer transfer requires self.crossOriginIsolated. + try { + new SharedArrayBuffer(4); + this.can_array_buffer = true; + } catch (e) { + this.can_array_buffer = false; + console.warn("SharedArrayBuffer is not supported:", e); + + if ( + !(globalThis as unknown as { crossOriginIsolated: unknown }) + .crossOriginIsolated + ) { + console.warn( + "SharedArrayBuffer is not supported because crossOriginIsolated is not enabled.", + ); + } + } + + if (this.can_array_buffer) { + this.park = new WASIFarmParkUseArrayBuffer( + this.fds_ref(), + stdin_, + stdout_, + stderr_, + default_allow_fds, + options?.allocator_size, + ); + } else { + throw new Error("Non SharedArrayBuffer is not supported yet"); + } + + this.park.listen(); + } + + private fds_ref(): Array { + const fds = new Proxy([] as Array, { + get: (_, prop) => { + if (prop === "push") { + return (fd: Fd) => { + const len = this.fds.push(fd); + return len; + }; + } + // @ts-ignore + return this.fds[prop]; + }, + + set: (_, prop, value) => { + // @ts-ignore + this.fds[prop] = value; + return true; + }, + }); + + return fds; + } + + get_ref(): WASIFarmRefObject { + return this.park.get_ref(); + } +} diff --git a/threads/src/index.ts b/threads/src/index.ts new file mode 100644 index 0000000..f5ec1d6 --- /dev/null +++ b/threads/src/index.ts @@ -0,0 +1,5 @@ +import { WASIFarmAnimal } from "./animals.js"; +import { WASIFarm } from "./farm.js"; +import { WASIFarmRef } from "./ref.js"; +export { thread_spawn_on_worker } from "./shared_array_buffer/index.js"; +export { WASIFarm, WASIFarmRef, WASIFarmAnimal }; diff --git a/threads/src/park.ts b/threads/src/park.ts new file mode 100644 index 0000000..b652648 --- /dev/null +++ b/threads/src/park.ts @@ -0,0 +1,593 @@ +import { type Fd, wasi } from "@bjorn3/browser_wasi_shim"; +import type { WASIFarmRefObject } from "./ref.js"; + +export abstract class WASIFarmPark { + abstract get_ref(): WASIFarmRefObject; + abstract listen(): void; + abstract notify_set_fd(fd: number): void; + abstract notify_rm_fd(fd: number): void; + abstract can_set_new_fd(fd: number): [boolean, Promise | undefined]; + + protected fds: Array; + protected stdin: number | undefined; + protected stdout: number | undefined; + protected stderr: number | undefined; + protected default_allow_fds: Array; + + constructor( + fds: Array, + stdin: number | undefined, + stdout: number | undefined, + stderr: number | undefined, + default_allow_fds: Array, + ) { + this.fds = fds; + this.stdin = stdin; + this.stdout = stdout; + this.stderr = stderr; + this.default_allow_fds = default_allow_fds; + this.fds_map = new Array(fds.length); + for (let i = 0; i < fds.length; i++) { + this.fds_map[i] = []; + } + // console.log("first fds_map", this.fds_map); + } + + private get_new_fd_lock = new Array<() => Promise>(); + + // fdに対して、現在そのfdにidがアクセス可能かを示す。 + protected fds_map: Array; + + // If the reassigned value is accessed after being closed, + // it will be strange, + // but the programmer should have written it + // so that this does not happen in the first place. + private async get_new_fd(): Promise<[() => Promise, number]> { + const promise = new Promise<[() => Promise, number]>((resolve) => { + const len = this.get_new_fd_lock.push(async () => { + let ret = -1; + for (let i = 0; i < this.fds.length; i++) { + if (this.fds[i] === undefined) { + ret = i; + break; + } + } + if (ret === -1) { + ret = this.fds.length; + // console.log("push_fd", this.fds.length) + (this.fds as Array).push(undefined); + this.fds_map.push([]); + // console.log("push_fd", this.fds.length) + } + + const [can, promise] = this.can_set_new_fd(ret); + if (!can) { + await promise; + } + + // If it's assigned, it's resolved. + resolve([ + async () => { + this.get_new_fd_lock.shift(); + const fn = this.get_new_fd_lock[0]; + if (fn !== undefined) { + fn(); + } + // assigned and notify + await this.notify_set_fd(ret); + }, + ret, + ]); + }); + if (len === 1) { + this.get_new_fd_lock[0](); + } + }); + return promise; + } + + protected fd_advise(fd: number): number { + if (this.fds[fd] !== undefined) { + return wasi.ERRNO_SUCCESS; + } + return wasi.ERRNO_BADF; + } + + protected fd_allocate(fd: number, offset: bigint, len: bigint): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].fd_allocate(offset, len); + } + return wasi.ERRNO_BADF; + } + + protected async fd_close(fd: number): Promise { + if (this.fds[fd] !== undefined) { + const ret = this.fds[fd].fd_close(); + (this.fds as Array)[fd] = undefined; + // console.log("fd_close1", fd); + await this.notify_rm_fd(fd); + // console.log("fd_close2", fd); + return ret; + } + return wasi.ERRNO_BADF; + } + + protected fd_datasync(fd: number): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].fd_sync(); + } + return wasi.ERRNO_BADF; + } + + protected fd_fdstat_get(fd: number): [wasi.Fdstat | undefined, number] { + if (this.fds[fd] !== undefined) { + const { ret, fdstat } = this.fds[fd].fd_fdstat_get(); + if (fdstat != null) { + return [fdstat, ret]; + } + return [undefined, ret]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected fd_fdstat_set_flags(fd: number, flags: number): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].fd_fdstat_set_flags(flags); + } + return wasi.ERRNO_BADF; + } + + protected fd_fdstat_set_rights( + fd: number, + fs_rights_base: bigint, + fs_rights_inheriting: bigint, + ): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].fd_fdstat_set_rights( + fs_rights_base, + fs_rights_inheriting, + ); + } + return wasi.ERRNO_BADF; + } + + protected fd_filestat_get(fd: number): [wasi.Filestat | undefined, number] { + if (this.fds[fd] !== undefined) { + const { ret, filestat } = this.fds[fd].fd_filestat_get(); + if (filestat != null) { + return [filestat, ret]; + } + return [undefined, ret]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected fd_filestat_set_size(fd: number, size: bigint): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].fd_filestat_set_size(size); + } + return wasi.ERRNO_BADF; + } + + protected fd_filestat_set_times( + fd: number, + atim: bigint, + mtim: bigint, + fst_flags: number, + ): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].fd_filestat_set_times(atim, mtim, fst_flags); + } + return wasi.ERRNO_BADF; + } + + protected fd_pread( + fd: number, + iovecs: Array, + offset: bigint, + ): [[number, Uint8Array] | undefined, number] { + if (this.fds[fd] !== undefined) { + let nread = 0; + + let buffer8 = new Uint8Array(0); + for (const iovec of iovecs) { + const { ret, data } = this.fds[fd].fd_pread(iovec.buf_len, offset); + if (ret !== wasi.ERRNO_SUCCESS) { + return [[nread, buffer8], ret]; + } + const new_buffer = new Uint8Array(buffer8.byteLength + data.byteLength); + new_buffer.set(buffer8); + new_buffer.set(data, buffer8.byteLength); + buffer8 = new_buffer; + nread += data.byteLength; + if (data.byteLength !== iovec.buf_len) { + break; + } + } + return [[nread, buffer8], wasi.ERRNO_SUCCESS]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected fd_prestat_get(fd: number): [wasi.Prestat | undefined, number] { + if (this.fds[fd] !== undefined) { + const { ret, prestat } = this.fds[fd].fd_prestat_get(); + if (prestat != null) { + return [prestat, ret]; + } + return [undefined, ret]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected fd_prestat_dir_name( + fd: number, + path_len: number, + ): [Uint8Array | undefined, number] { + if (this.fds[fd] !== undefined) { + const { ret, prestat } = this.fds[fd].fd_prestat_get(); + if (prestat) { + const prestat_dir_name = prestat.inner.pr_name; + + // console.log("fd_prestat_dir_name: park: inner: ", prestat_dir_name); + // console.log("fd_prestat_dir_name: park: inner: ", new TextDecoder().decode(prestat_dir_name)); + + // console.log("fd_prestat_dir_name: park: path_len: ", path_len); + + if (prestat_dir_name.length <= path_len) { + // console.log("fd_prestat_dir_name: park: A"); + return [prestat_dir_name, ret]; + } + + // console.log("fd_prestat_dir_name: park: B"); + return [prestat_dir_name.slice(0, path_len), wasi.ERRNO_NAMETOOLONG]; + } + // console.log("fd_prestat_dir_name: park: C"); + return [undefined, ret]; + } + // console.log("fd_prestat_dir_name: park: D"); + return [undefined, wasi.ERRNO_BADF]; + } + + protected fd_pwrite( + fd: number, + write_data: Uint8Array, + offset: bigint, + ): [number | undefined, number] { + if (this.fds[fd] !== undefined) { + const { ret, nwritten } = this.fds[fd].fd_pwrite(write_data, offset); + return [nwritten, ret]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected fd_read( + fd: number, + iovecs: Array, + ): [[number, Uint8Array] | undefined, number] { + if (this.fds[fd] !== undefined) { + let nread = 0; + + // console.log("fd_read: park: iovecs: ", iovecs); + + // const sum_len = iovecs.reduce((acc, iovec) => acc + iovec.buf_len, 0); + + // console.warn("fd_read: park: sum_len: ", sum_len); + + let buffer8 = new Uint8Array(0); + for (const iovec of iovecs) { + const { ret, data } = this.fds[fd].fd_read(iovec.buf_len); + // console.log("fd_read: park: data: ", data); + if (ret !== wasi.ERRNO_SUCCESS) { + return [[nread, buffer8], ret]; + } + const new_buffer = new Uint8Array(buffer8.byteLength + data.byteLength); + new_buffer.set(buffer8); + new_buffer.set(data, buffer8.byteLength); + buffer8 = new_buffer; + nread += data.byteLength; + if (data.byteLength !== iovec.buf_len) { + break; + } + } + + // console.log("fd_read: park: nread: ", nread); + + return [[nread, buffer8], wasi.ERRNO_SUCCESS]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected fd_readdir( + fd: number, + buf_len: number, + cookie: bigint, + ): [[Uint8Array, number] | undefined, number] { + if (this.fds[fd] !== undefined) { + const array = new Uint8Array(buf_len); + + let buf_used = 0; + let offset = 0; + + // eslint-disable-next-line no-constant-condition + while (true) { + const { ret, dirent } = this.fds[fd].fd_readdir_single(cookie); + if (ret !== wasi.ERRNO_SUCCESS) { + return [[array, buf_used], ret]; + } + if (dirent == null) { + break; + } + if (buf_len - buf_used < dirent.head_length()) { + buf_used = buf_len; + break; + } + + const head_bytes = new ArrayBuffer(dirent.head_length()); + dirent.write_head_bytes(new DataView(head_bytes), 0); + array.set( + new Uint8Array(head_bytes).slice( + 0, + Math.min(head_bytes.byteLength, buf_len - buf_used), + ), + offset, + ); + offset += head_bytes.byteLength; + buf_used += head_bytes.byteLength; + + if (buf_len - buf_used < dirent.name_length()) { + buf_used = buf_len; + break; + } + + dirent.write_name_bytes(array, offset, buf_len - buf_used); + offset += dirent.name_length(); + buf_used += dirent.name_length(); + + // biome-ignore lint/style/noParameterAssign: + cookie = dirent.d_next; + } + + return [[array, buf_used], wasi.ERRNO_SUCCESS]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + // protected async fd_renumber(fd: number, to: number): Promise { + // if (this.fds[fd] != undefined) { + // const ret = this.fds[to].fd_close(); + // if (ret != wasi.ERRNO_SUCCESS) { + // return ret; + // } + // this.fds[to] = this.fds[fd]; + // this.fds[fd] = undefined; + // await this.notify_rm_fd(fd); + // return wasi.ERRNO_SUCCESS; + // } else { + // return wasi.ERRNO_BADF; + // } + // } + + protected fd_seek( + fd: number, + offset: bigint, + whence: number, + ): [bigint | undefined, number] { + if (this.fds[fd] !== undefined) { + const { ret, offset: new_offset } = this.fds[fd].fd_seek(offset, whence); + return [new_offset, ret]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected fd_sync(fd: number): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].fd_sync(); + } + return wasi.ERRNO_BADF; + } + + protected fd_tell(fd: number): [bigint | undefined, number] { + if (this.fds[fd] !== undefined) { + const { ret, offset } = this.fds[fd].fd_tell(); + return [offset, ret]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected async fd_write( + fd: number, + write_data: Uint8Array, + ): Promise<[number | undefined, number]> { + if (this.fds[fd] !== undefined) { + const fd_ret = this.fds[fd].fd_write(write_data); + let ret: number; + let nwritten: number; + if (fd_ret instanceof Promise) { + // @ts-ignore + ({ ret, nwritten } = await fd_ret); + } else { + ({ ret, nwritten } = fd_ret); + } + return [nwritten, ret]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected path_create_directory(fd: number, path: string): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].path_create_directory(path); + } + return wasi.ERRNO_BADF; + } + + protected path_filestat_get( + fd: number, + flags: number, + path: string, + ): [wasi.Filestat | undefined, number] { + if (this.fds[fd] !== undefined) { + const { ret, filestat } = this.fds[fd].path_filestat_get(flags, path); + if (filestat != null) { + return [filestat, ret]; + } + return [undefined, ret]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected path_filestat_set_times( + fd: number, + flags: number, + path: string, + atim: bigint, + mtim: bigint, + fst_flags: number, + ): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].path_filestat_set_times( + flags, + path, + atim, + mtim, + fst_flags, + ); + } + return wasi.ERRNO_BADF; + } + + protected path_link( + old_fd: number, + old_flags: number, + old_path: string, + new_fd: number, + new_path: string, + ): number { + if (this.fds[old_fd] !== undefined && this.fds[new_fd] !== undefined) { + const { ret, inode_obj } = this.fds[old_fd].path_lookup( + old_path, + old_flags, + ); + if (inode_obj == null) { + return ret; + } + return this.fds[new_fd].path_link(new_path, inode_obj, false); + } + return wasi.ERRNO_BADF; + } + + protected async path_open( + fd: number, + dirflags: number, + path: string, + oflags: number, + fs_rights_base: bigint, + fs_rights_inheriting: bigint, + fs_flags: number, + ): Promise<[number | undefined, number]> { + if (this.fds[fd] !== undefined) { + const { ret, fd_obj } = this.fds[fd].path_open( + dirflags, + path, + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + ); + // console.log("path_open: park: ", ret, fd_obj); + if (ret !== wasi.ERRNO_SUCCESS) { + return [undefined, ret]; + } + + const [resolve, opened_fd] = await this.get_new_fd(); + + // console.log("path_open: park: ", path, "opened_fd" ,opened_fd); + + if (!fd_obj) { + throw "fd_obj should not be null"; + } + + this.fds[opened_fd] = fd_obj; + + await resolve(); + + // console.log("path_open: park: len: ", len); + + // console.log("path_open: park: ", opened_fd); + + return [opened_fd, wasi.ERRNO_SUCCESS]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected path_readlink( + fd: number, + path: string, + buf_len: number, + ): [Uint8Array | undefined, number] { + if (this.fds[fd] !== undefined) { + const { ret, data } = this.fds[fd].path_readlink(path); + if (data != null) { + const data_buf = new TextEncoder().encode(data); + if (data_buf.byteLength > buf_len) { + // wasi.ts use ERRNO_BADF. I think it should be ERRNO_OVERFLOW. + return [data_buf.slice(0, buf_len), wasi.ERRNO_OVERFLOW]; + } + return [data_buf, ret]; + } + return [undefined, ret]; + } + return [undefined, wasi.ERRNO_BADF]; + } + + protected path_remove_directory(fd: number, path: string): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].path_remove_directory(path); + } + return wasi.ERRNO_BADF; + } + + protected path_rename( + old_fd: number, + old_path: string, + new_fd: number, + new_path: string, + ): number { + if (this.fds[old_fd] !== undefined && this.fds[new_fd] !== undefined) { + // eslint-disable-next-line prefer-const + let { ret, inode_obj } = this.fds[old_fd].path_unlink(old_path); + if (inode_obj == null) { + return ret; + } + ret = this.fds[new_fd].path_link(new_path, inode_obj, true); + if (ret !== wasi.ERRNO_SUCCESS) { + if ( + this.fds[old_fd].path_link(old_path, inode_obj, true) !== + wasi.ERRNO_SUCCESS + ) { + throw "path_link should always return success when relinking an inode back to the original place"; + } + } + return ret; + } + return wasi.ERRNO_BADF; + } + + protected path_symlink( + _old_path: string, + fd: number, + _new_path: string, + ): number { + if (this.fds[fd] !== undefined) { + return wasi.ERRNO_NOTSUP; + } + return wasi.ERRNO_BADF; + } + + protected path_unlink_file(fd: number, path: string): number { + if (this.fds[fd] !== undefined) { + return this.fds[fd].path_unlink_file(path); + } + return wasi.ERRNO_BADF; + } +} diff --git a/threads/src/polyfill.js b/threads/src/polyfill.js new file mode 100644 index 0000000..bde01dd --- /dev/null +++ b/threads/src/polyfill.js @@ -0,0 +1,147 @@ +// https://github.com/tc39/proposal-atomics-wait-async/blob/master/polyfill.js + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Author: Lars T Hansen, lhansen@mozilla.com + */ + +/* Polyfill for Atomics.waitAsync() for web browsers. + * + * Any kind of agent that is able to create a new Worker can use this polyfill. + * + * Load this file in all agents that will use Atomics.waitAsync. + * + * Agents that don't call Atomics.waitAsync need do nothing special. + * + * Any kind of agent can wake another agent that is sleeping in + * Atomics.waitAsync by just calling Atomics.notify for the location being slept + * on, as normal. + * + * The implementation is not completely faithful to the proposed semantics: in + * the case where an agent first asyncWaits and then waits on the same location: + * when it is woken, the two waits will be woken in order, while in the real + * semantics, the sync wait will be woken first. + * + * In this polyfill Atomics.waitAsync is not very fast. + */ + +/* Implementation: + * + * For every wait we fork off a Worker to perform the wait. Workers are reused + * when possible. The worker communicates with its parent using postMessage. + */ + +(() => { + if (typeof Atomics.waitAsync === "function") return; + + const helperCode = ` + onmessage = function (ev) { + try { + switch (ev.data[0]) { + case 'wait': { + let [_, ia, index, value, timeout] = ev.data; + let result = Atomics.wait(ia, index, value, timeout) + postMessage(['ok', result]); + break; + } + default: { + throw new Error("Bogus message sent to wait helper: " + ev.data.join(',')); + } + } + } catch (e) { + console.log("Exception in wait helper"); + postMessage(['error', 'Exception']); + } + } + `; + + const helpers = []; + + function allocHelper() { + if (helpers.length > 0) return helpers.pop(); + const h = new Worker( + `data:application/javascript,${encodeURIComponent(helperCode)}`, + ); + return h; + } + + function freeHelper(h) { + helpers.push(h); + } + + // Atomics.waitAsync always returns a promise. Throws standard errors + // for parameter validation. The promise is resolved with a string as from + // Atomics.wait, or, in the case something went completely wrong, it is + // rejected with an error string. + + function waitAsync(ia, index_, value_, timeout_) { + if ( + typeof ia !== "object" || + !(ia instanceof Int32Array) || + !(ia.buffer instanceof SharedArrayBuffer) + ) + throw new TypeError("Expected shared memory"); + + // These conversions only approximate the desired semantics but are + // close enough for the polyfill. + + const index = index_ | 0; + const value = value_ | 0; + const timeout = + timeout_ === undefined ? Number.POSITIVE_INFINITY : +timeout_; + + // Range checking for the index. + + ia[index]; + + // Optimization, avoid the helper thread in this common case. + + if (Atomics.load(ia, index) !== value) return Promise.resolve("not-equal"); + + // General case, we must wait. + + return new Promise((resolve, reject) => { + const h = allocHelper(); + h.onmessage = (ev) => { + // Free the helper early so that it can be reused if the resolution + // needs a helper. + freeHelper(h); + switch (ev.data[0]) { + case "ok": + resolve(ev.data[1]); + break; + case "error": + // Note, rejection is not in the spec, it is an artifact of the polyfill. + // The helper already printed an error to the console. + reject(ev.data[1]); + break; + } + }; + + // It's possible to do better here if the ia is already known to the + // helper. In that case we can communicate the other data through + // shared memory and wake the agent. And it is possible to make ia + // known to the helper by waking it with a special value so that it + // checks its messages, and then posting the ia to the helper. Some + // caching / decay scheme is useful no doubt, to improve performance + // and avoid leaks. + // + // In the event we wake the helper directly, we can micro-wait here + // for a quick result. We'll need to restructure some code to make + // that work out properly, and some synchronization is necessary for + // the helper to know that we've picked up the result and no + // postMessage is necessary. + + h.postMessage(["wait", ia, index, value, timeout]); + }); + } + + Object.defineProperty(Atomics, "waitAsync", { + value: waitAsync, + configurable: true, + enumerable: false, + writable: true, + }); +})(); diff --git a/threads/src/ref.ts b/threads/src/ref.ts new file mode 100644 index 0000000..a7dce7f --- /dev/null +++ b/threads/src/ref.ts @@ -0,0 +1,186 @@ +import type { FdCloseSender } from "./sender.js"; +import type { wasi } from "@bjorn3/browser_wasi_shim"; + +export type WASIFarmRefObject = { + stdin: number | undefined; + stdout: number | undefined; + stderr: number | undefined; + fd_close_receiver: FdCloseSender; + default_fds: Array; +}; + +export abstract class WASIFarmRef { + abstract get_fds_len(): number; + // please implement this method + // abstract init_self(sl: WASIFarmRef): WASIFarmRef; + + protected stdin: number | undefined; + protected stdout: number | undefined; + protected stderr: number | undefined; + + protected id!: number; + + fd_close_receiver: FdCloseSender; + + default_fds: Array = []; + + async send(targets: Array, fd: number): Promise { + await this.fd_close_receiver.send(targets, fd); + } + + get(id: number): Array | undefined { + return this.fd_close_receiver.get(id); + } + + abstract set_park_fds_map(fds: Array): void; + + abstract set_id(): number; + + constructor( + stdin: number | undefined, + stdout: number | undefined, + stderr: number | undefined, + fd_close_receiver: FdCloseSender, + default_fds?: Array, + ) { + this.stdin = stdin; + this.stdout = stdout; + this.stderr = stderr; + this.fd_close_receiver = fd_close_receiver; + if (default_fds !== undefined) { + this.default_fds = default_fds; + } + } + + get_stdin(): number | undefined { + return this.stdin; + } + + get_stdout(): number | undefined { + return this.stdout; + } + + get_stderr(): number | undefined { + return this.stderr; + } + + abstract fd_advise(fd: number | undefined): number; + abstract fd_allocate( + fd: number | undefined, + offset: bigint, + len: bigint, + ): number; + abstract fd_close(fd: number | undefined): number; + abstract fd_datasync(fd: number | undefined): number; + abstract fd_fdstat_get( + fd: number | undefined, + ): [wasi.Fdstat | undefined, number]; + abstract fd_fdstat_set_flags(fd: number | undefined, flags: number): number; + abstract fd_fdstat_set_rights( + fd: number | undefined, + fs_rights_base: bigint, + fs_rights_inheriting: bigint, + ): number; + abstract fd_filestat_get( + fd: number | undefined, + ): [wasi.Filestat | undefined, number]; + abstract fd_filestat_set_size(fd: number | undefined, size: bigint): number; + abstract fd_filestat_set_times( + fd: number | undefined, + atim: bigint, + mtim: bigint, + fst_flags: number, + ): number; + abstract fd_pread( + fd: number | undefined, + iovs: Uint32Array, + offset: bigint, + ): [[number, Uint8Array] | undefined, number]; + abstract fd_prestat_get( + fd: number | undefined, + ): [[number, number] | undefined, number]; + abstract fd_prestat_dir_name( + fd: number | undefined, + path_len: number, + ): [Uint8Array | undefined, number]; + abstract fd_pwrite( + fd: number | undefined, + iovs: Uint8Array, + offset: bigint, + ): [number | undefined, number]; + abstract fd_read( + fd: number | undefined, + iovs: Uint32Array, + ): [[number, Uint8Array] | undefined, number]; + abstract fd_readdir( + fd: number | undefined, + limit_buf_len: number, + cookie: bigint, + ): [[Uint8Array, number] | undefined, number]; + // abstract fd_renumber(fd: number | undefined, to: number): number; + abstract fd_seek( + fd: number | undefined, + offset: bigint, + whence: number, + ): [bigint | undefined, number]; + abstract fd_sync(fd: number | undefined): number; + abstract fd_tell(fd: number | undefined): [bigint | undefined, number]; + abstract fd_write( + fd: number | undefined, + iovs: Uint8Array, + ): [number | undefined, number]; + abstract path_create_directory( + fd: number | undefined, + path: Uint8Array, + ): number; + abstract path_filestat_get( + fd: number | undefined, + flags: number, + path: Uint8Array, + ): [wasi.Filestat | undefined, number]; + abstract path_filestat_set_times( + fd: number | undefined, + flags: number, + path: Uint8Array, + st_atim: bigint, + st_mtim: bigint, + fst_flags: number, + ): number; + abstract path_link( + old_fd: number | undefined, + old_flags: number, + old_path: Uint8Array, + new_fd: number | undefined, + new_path: Uint8Array, + ): number; + abstract path_open( + fd: number | undefined, + dirflags: number, + path: Uint8Array, + oflags: number, + fs_rights_base: bigint, + fs_rights_inheriting: bigint, + fs_flags: number, + ): [number | undefined, number]; + abstract path_readlink( + fd: number | undefined, + path: Uint8Array, + buf_len: number, + ): [Uint8Array | undefined, number]; + abstract path_remove_directory( + fd: number | undefined, + path: Uint8Array, + ): number; + abstract path_rename( + old_fd: number | undefined, + old_path: Uint8Array, + new_fd: number | undefined, + new_path: Uint8Array, + ): number; + abstract path_symlink( + old_path: Uint8Array, + fd: number | undefined, + new_path: Uint8Array, + ): number; + abstract path_unlink_file(fd: number | undefined, path: Uint8Array): number; +} diff --git a/threads/src/sender.ts b/threads/src/sender.ts new file mode 100644 index 0000000..ed9d30a --- /dev/null +++ b/threads/src/sender.ts @@ -0,0 +1,4 @@ +export interface FdCloseSender { + send(targets: Array, fd: number): Promise; + get(id: number): Array | undefined; +} diff --git a/threads/src/shared_array_buffer/allocator.ts b/threads/src/shared_array_buffer/allocator.ts new file mode 100644 index 0000000..98ed3db --- /dev/null +++ b/threads/src/shared_array_buffer/allocator.ts @@ -0,0 +1,211 @@ +// @ts-ignore +// import { debug } from "../../debug.js"; +// import "../polyfill.js"; + +export type AllocatorUseArrayBufferObject = { + share_arrays_memory: SharedArrayBuffer; +}; + +export class AllocatorUseArrayBuffer { + // Pass a !Sized type + // The first 4 bytes are for a lock value: i32 + // The next 4 bytes are for the current number of arrays: m: i32 + // The next 4 bytes are for the length of the occupied space in share_arrays_memory: n: i32 + // Once it is no longer busy, it should become empty immediately, so reset only when it is empty. + // Even if it becomes too long, it should be fine due to the browser's virtualization. + // Using an algorithm even simpler than First-Fit + // SharedArrayBuffer.grow is supported by all major browsers except Android WebView, + // which does not support SharedArrayBuffer in the first place, + // but es2024 and the type system does not support it, + // so the size is fixed from the beginning + + // share_arrays_memory: SharedArrayBuffer = new SharedArrayBuffer(12, { + // // 10MB + // maxByteLength: 10 * 1024 * 1024, + // }); + + // Even if 100MB is allocated, due to browser virtualization, + // the memory should not actually be used until it is needed. + share_arrays_memory: SharedArrayBuffer; + + // When adding data, use Atomics.wait to wait until the first 4 bytes become 0. + // After that, use Atomics.compareExchange to set the first 4 bytes to 1. + // Then, use Atomics.add to increment the next 4 bytes by 1. + // If the return value is 0, proceed to *1. + // If the return value is 1, use Atomics.wait to wait until the first 4 bytes become 0. + // *1: Increment the second by 1 using Atomics.add. If the return value is 0, reset it. + // Add the data. Extend if there is not enough space. + // To release, just decrement by 1 using Atomics.sub. + + // Since postMessage makes the class an object, + // it must be able to receive and assign a SharedArrayBuffer. + constructor( + share_arrays_memory: SharedArrayBuffer = new SharedArrayBuffer( + 10 * 1024 * 1024, + ), + ) { + this.share_arrays_memory = share_arrays_memory; + const view = new Int32Array(this.share_arrays_memory); + Atomics.store(view, 0, 0); + Atomics.store(view, 1, 0); + Atomics.store(view, 2, 12); + } + + // Since postMessage converts classes to objects, + // it must be able to convert objects to classes. + static init_self(sl: AllocatorUseArrayBufferObject): AllocatorUseArrayBuffer { + return new AllocatorUseArrayBuffer(sl.share_arrays_memory); + } + + // Writes without blocking threads when acquiring locks + async async_write( + data: Uint8Array | Uint32Array, + memory: SharedArrayBuffer, + // ptr, len + // Pass I32Array ret_ptr + ret_ptr: number, + ): Promise<[number, number]> { + const view = new Int32Array(this.share_arrays_memory); + // eslint-disable-next-line no-constant-condition + while (true) { + let lock: "not-equal" | "timed-out" | "ok"; + const { value } = Atomics.waitAsync(view, 0, 1); + if (value instanceof Promise) { + lock = await value; + } else { + lock = value; + } + if (lock === "timed-out") { + throw new Error("timed-out lock"); + } + const old = Atomics.compareExchange(view, 0, 0, 1); + if (old !== 0) { + continue; + } + + const ret = this.write_inner(data, memory, ret_ptr); + + // release lock + Atomics.store(view, 0, 0); + Atomics.notify(view, 0, 1); + + return ret; + } + } + + // Blocking threads for writing when acquiring locks + block_write( + data: Uint8Array | Uint32Array, + memory: SharedArrayBuffer, + // ptr, len + ret_ptr: number, + ): [number, number] { + // eslint-disable-next-line no-constant-condition + while (true) { + const view = new Int32Array(this.share_arrays_memory); + const lock = Atomics.wait(view, 0, 1); + if (lock === "timed-out") { + throw new Error("timed-out lock"); + } + const old = Atomics.compareExchange(view, 0, 0, 1); + if (old !== 0) { + continue; + } + + const ret = this.write_inner(data, memory, ret_ptr); + + // release lock + Atomics.store(view, 0, 0); + Atomics.notify(view, 0, 1); + + return ret; + } + } + + // Function to write after acquiring a lock + write_inner( + data: Uint8Array | Uint32Array, + memory: SharedArrayBuffer, + // ptr, len + ret_ptr: number, + ): [number, number] { + // console.log("data", data); + + const view = new Int32Array(this.share_arrays_memory); + const view8 = new Uint8Array(this.share_arrays_memory); + + // Indicates more users using memory + const old_num = Atomics.add(view, 1, 1); + let share_arrays_memory_kept: number; + if (old_num === 0) { + // Reset because there were no users. + // debug.log("reset allocator"); + share_arrays_memory_kept = Atomics.store(view, 2, 12); + } else { + share_arrays_memory_kept = Atomics.load(view, 2); + } + // console.log("num", Atomics.load(view, 1)); + + const memory_len = this.share_arrays_memory.byteLength; + const len = data.byteLength; + const new_memory_len = share_arrays_memory_kept + len; + if (memory_len < new_memory_len) { + // extend memory + // support from es2024 + // this.share_arrays_memory.grow(new_memory_len); + throw new Error( + "size is bigger than memory. \nTODO! fix memory limit. support big size another way.", + ); + } + + let data8: Uint8Array; + if (data instanceof Uint8Array) { + data8 = data; + } else if (data instanceof Uint32Array) { + // data to uint8 + const tmp = new ArrayBuffer(data.byteLength); + new Uint32Array(tmp).set(data); + data8 = new Uint8Array(tmp); + } + + // biome-ignore lint/style/noNonNullAssertion: + view8.set(new Uint8Array(data8!), share_arrays_memory_kept); + Atomics.store(view, 2, new_memory_len); + + const memory_view = new Int32Array(memory); + Atomics.store(memory_view, ret_ptr, share_arrays_memory_kept); + Atomics.store(memory_view, ret_ptr + 1, len); + + // console.log("allocator: allocate", share_arrays_memory_kept, len); + + return [share_arrays_memory_kept, len]; + } + + // free allocated memory + free(_pointer: number, _len: number) { + Atomics.sub(new Int32Array(this.share_arrays_memory), 1, 1); + + // console.log("allocator: free", pointer, len); + } + + // get memory from pointer and length + get_memory(ptr: number, len: number): ArrayBuffer { + const data = new ArrayBuffer(len); + const view = new Uint8Array(data); + view.set(new Uint8Array(this.share_arrays_memory).slice(ptr, ptr + len)); + return data; + } + + // Write again to the memory before releasing + // Not used because the situation for using it does not exist. + use_defined_memory(ptr: number, len: number, data: ArrayBufferLike) { + const memory = new Uint8Array(this.share_arrays_memory); + memory.set(new Uint8Array(data).slice(0, len), ptr); + } + + get_object(): AllocatorUseArrayBufferObject { + return { + share_arrays_memory: this.share_arrays_memory, + }; + } +} diff --git a/threads/src/shared_array_buffer/fd_close_sender.ts b/threads/src/shared_array_buffer/fd_close_sender.ts new file mode 100644 index 0000000..a137e77 --- /dev/null +++ b/threads/src/shared_array_buffer/fd_close_sender.ts @@ -0,0 +1,62 @@ +import type { FdCloseSender } from "../sender.js"; +import { + ToRefSenderUseArrayBuffer, + type ToRefSenderUseArrayBufferObject, +} from "./sender.js"; + +export type FdCloseSenderUseArrayBufferObject = { + max_share_arrays_memory?: number; + share_arrays_memory?: SharedArrayBuffer; +} & ToRefSenderUseArrayBufferObject; + +// Object to tell other processes, +// such as child processes, +// that the file descriptor has been closed +export class FdCloseSenderUseArrayBuffer + extends ToRefSenderUseArrayBuffer + implements FdCloseSender +{ + // Should be able to change the size of memory as it accumulates more and more on memory + constructor( + max_share_arrays_memory?: number, + share_arrays_memory?: SharedArrayBuffer, + ) { + super(4, max_share_arrays_memory, share_arrays_memory); + } + + // Send the closed file descriptor to the target process + async send(targets: Array, fd: number): Promise { + if (targets === undefined || targets.length === 0) { + throw new Error("targets is empty"); + } + // console.log("fd_close_sender send", targets, fd); + + await this.async_send(targets, new Uint32Array([fd])); + } + + // Get the closed file descriptor from the target process + get(id: number): Array | undefined { + const data = this.get_data(id); + if (data === undefined) { + return undefined; + } + + // console.log("fd_close_sender get", data); + + const array = []; + for (const i of data) { + array.push(i[0]); + } + + return array; + } + + // Initialize the class from object + static init_self(sl: FdCloseSenderUseArrayBufferObject): FdCloseSender { + const sel = ToRefSenderUseArrayBuffer.init_self_inner(sl); + return new FdCloseSenderUseArrayBuffer( + sel.max_share_arrays_memory, + sel.share_arrays_memory, + ); + } +} diff --git a/threads/src/shared_array_buffer/index.ts b/threads/src/shared_array_buffer/index.ts new file mode 100644 index 0000000..0e2d34a --- /dev/null +++ b/threads/src/shared_array_buffer/index.ts @@ -0,0 +1,13 @@ +import { WASIFarmParkUseArrayBuffer } from "./park.js"; +import { WASIFarmRefUseArrayBuffer } from "./ref.js"; +import type { WASIFarmRefUseArrayBufferObject } from "./ref.js"; +import { ThreadSpawner } from "./thread_spawn.js"; +import { thread_spawn_on_worker } from "./thread_spawn.js"; + +export { + WASIFarmRefUseArrayBuffer, + type WASIFarmRefUseArrayBufferObject, + WASIFarmParkUseArrayBuffer, + ThreadSpawner, + thread_spawn_on_worker, +}; diff --git a/threads/src/shared_array_buffer/park.ts b/threads/src/shared_array_buffer/park.ts new file mode 100644 index 0000000..cd77671 --- /dev/null +++ b/threads/src/shared_array_buffer/park.ts @@ -0,0 +1,1000 @@ +import { type Fd, wasi } from "@bjorn3/browser_wasi_shim"; +import { WASIFarmPark } from "../park.js"; +import type { FdCloseSender } from "../sender.js"; +import { AllocatorUseArrayBuffer } from "./allocator.js"; +import { FdCloseSenderUseArrayBuffer } from "./fd_close_sender.js"; +import type { WASIFarmRefUseArrayBufferObject } from "./ref.js"; +import { get_func_name_from_number } from "./util.js"; + +export const fd_func_sig_u32_size: number = 18; +export const fd_func_sig_bytes: number = fd_func_sig_u32_size * 4; + +export class WASIFarmParkUseArrayBuffer extends WASIFarmPark { + private allocator: AllocatorUseArrayBuffer; + + // args and env do not change, so copying them is fine. + // Functions that do not depend on fds are skipped. + // Since it is wasm32, the pointer is u32. + // errno is u8. + // https://github.com/WebAssembly/WASI/blob/4feaf733e946c375b610cc5d39ea2e1a68046e62/legacy/preview1/docs.md + // The first item is the function signature, and the second (if it exists) represents data that must be communicated in Park. If they are the same, it is not written. + // From here, direct access to fd begins. + // fd_advise: (fd: u32, offset: u64, len: u64, advice: u8) => errno; + // (fd: u32) => errno; + // fd_allocate: (fd: u32, offset: u64, len: u64) => errno; + // fd_close: (fd: u32) => errno; + // fd_datasync: (fd: u32) => errno; + // fd_fdstat_get: (fd: u32, fdstat_ptr: pointer) => errno; + // (fd: u32) => [wasi.Fdstat(u32 * 6)], errno]; + // fd_fdstat_set_flags: (fd: u32, flags: u16) => errno; + // fd_fdstat_set_rights: (fd: u32, fs_rights_base: u64, fs_rights_inheriting: u64) => errno; + // fd_filestat_get: (fd: u32, filestat_ptr: pointer) => errno; + // (fd: u32) => [wasi.Filestat(u32 * 16)], errno]; + // fd_filestat_set_size: (fd: u32, size: u64) => errno; + // fd_filestat_set_times: (fd: u32, atim: u64, mtim: u64, fst_flags: u16) => errno; + // fd_pread: (fd: u32, iovs_ptr: pointer, iovs_len: u32, offset: u64) => [u32, errno]; + // use share_arrays_memory; + // (fd: u32, iovs_ptr: pointer, iovs_len: u32, offset: u64) => [u32, data_ptr, errno]; + // fd_prestat_get: (fd: u32, prestat_ptr: pointer) => errno; + // (fd: u32) => [wasi.Prestat(u32 * 2)], errno]; + // fd_prestat_dir_name: (fd: u32, path_ptr: pointer, path_len: u32) => errno; + // (fd: u32, path_len: u32) => [path_ptr: pointer, path_len: u32, errno]; + // fd_pwrite: (fd: u32, iovs_ptr: pointer, iovs_len: u32, offset: u64) => [u32, errno]; + // use share_arrays_memory; + // (fd: u32, write_data: pointer, write_data_len: u32, offset: u64) => [u32, errno]; + // fd_read: (fd: u32, iovs_ptr: pointer, iovs_len: u32) => [u32, errno]; + // use share_arrays_memory; + // (fd: u32, iovs_ptr: pointer, iovs_len: u32) => [u32, data_ptr, errno]; + // fd_readdir: (fd: u32, buf_ptr: pointer, buf_len: u32, cookie: u64) => [u32, errno]; + // use share_arrays_memory; + // (fd: u32, buf_len: u32, cookie: u64) => [buf_ptr: pointer, buf_len: u32, buf_used: u32, errno]; + // fd_renumber: (fd: u32, to: u32) => errno; + // fd_seek: (fd: u32, offset: i64, whence: u8) => [u64, errno]; + // fd_sync: (fd: u32) => errno; + // fd_tell: (fd: u32) => [u64, errno]; + // fd_write: (fd: u32, iovs_ptr: pointer, iovs_len: u32) => [u32, errno]; + // use share_arrays_memory; + // (fd: u32, write_data: pointer, write_data_len: u32) => [u32, errno]; + // path_create_directory: (fd: u32, path_ptr: pointer, path_len: u32) => errno; + // path_filestat_get: (fd: u32, flags: u32, path_ptr: pointer, path_len: u32) => [wasi.Filestat(u32 * 16), errno]; + // path_filestat_set_times: (fd: u32, flags: u32, path_ptr: pointer, path_len: u32, atim: u64, mtim: u64, fst_flags: u16) => errno; + // path_link: (old_fd: u32, old_flags: u32, old_path_ptr: pointer, old_path_len: u32, new_fd: u32, new_path_ptr: pointer, new_path_len: u32) => errno; + // path_open: (fd: u32, dirflags: u32, path_ptr: pointer, path_len: u32, oflags: u32, fs_rights_base: u64, fs_rights_inheriting: u64, fdflags: u16) => [u32, errno]; + // note: fdsにpushするが、既存のfdに影響しないので、競合しない。 + // path_readlink: (fd: u32, path_ptr: pointer, path_len: u32, buf_ptr: pointer, buf_len: u32) => [u32, errno]; + // use share_arrays_memory; + // (fd: u32, path_ptr: pointer, path_len: u32, buf_len: u32) => [buf_len: u32, data_ptr: pointer, data_len: u32, errno]; + // path_remove_directory: (fd: u32, path_ptr: pointer, path_len: u32) => errno; + // path_rename: (old_fd: u32, old_path_ptr: pointer, old_path_len: u32, new_fd: u32, new_path_ptr: pointer, new_path_len: u32) => errno; + // path_symlink: (old_path_ptr: pointer, old_path_len: u32, fd: u32, new_path_ptr: pointer, new_path_len: u32) => errno; + // path_unlink_file: (fd: u32, path_ptr: pointer, path_len: u32) => errno; + + // Lock when you want to use fd + // Array<[lock, call_func]> + private lock_fds: SharedArrayBuffer; + + // 1 bytes: fds.length + // 1 bytes: wasi_farm_ref num(id) + // Actually, as long as it is working properly, fds.length is not used + private fds_len_and_num: SharedArrayBuffer; + + // listen promise keep + private listen_fds: Array> = []; + + // The largest size is u32 * 18 + 1 + // Alignment is troublesome, so make it u32 * 18 + 4 + // In other words, one size is 76 bytes + private fd_func_sig: SharedArrayBuffer; + + // listen base handle keep + // @ts-ignore + private listen_base_handle!: Promise; + + // listen base lock and call etc + private base_func_util: SharedArrayBuffer; + + // tell other processes that the file descriptor has been closed + private fd_close_receiver: FdCloseSender; + + // this is not send by postMessage, + // so it is not necessary to keep shared_array_buffer + // this class is not used by user, + // to avoid mistakes, all constructors are now required to be passed in. + constructor( + fds: Array, + // stdin fd number + stdin: number | undefined, + // stdout fd number + stdout: number | undefined, + // stderr fd number + stderr: number | undefined, + // wasi_farm_ref default allow fds + default_allow_fds: Array, + allocator_size?: number, + ) { + super(fds, stdin, stdout, stderr, default_allow_fds); + + if (allocator_size === undefined) { + this.allocator = new AllocatorUseArrayBuffer(); + } else { + this.allocator = new AllocatorUseArrayBuffer( + new SharedArrayBuffer(allocator_size), + ); + } + const max_fds_len = 128; + this.lock_fds = new SharedArrayBuffer(4 * max_fds_len * 3); + this.fd_func_sig = new SharedArrayBuffer( + fd_func_sig_u32_size * 4 * max_fds_len, + ); + this.fds_len_and_num = new SharedArrayBuffer(8); + + const view = new Int32Array(this.fds_len_and_num); + Atomics.store(view, 0, fds.length); + Atomics.store(view, 1, 0); + + this.fd_close_receiver = new FdCloseSenderUseArrayBuffer(); + this.base_func_util = new SharedArrayBuffer(24); + } + + /// Send this return by postMessage. + get_ref(): WASIFarmRefUseArrayBufferObject { + return { + allocator: this.allocator, + lock_fds: this.lock_fds, + fds_len_and_num: this.fds_len_and_num, + fd_func_sig: this.fd_func_sig, + base_func_util: this.base_func_util, + fd_close_receiver: this.fd_close_receiver, + stdin: this.stdin, + stdout: this.stdout, + stderr: this.stderr, + default_fds: this.default_allow_fds, + }; + } + + // abstract methods implementation + // from fd set ex) path_open + // received and listen the fd + // and set fds.length + async notify_set_fd(fd: number) { + if (this.fds[fd] === undefined) { + throw new Error("fd is not defined"); + } + if (fd >= 128) { + throw new Error("fd is too big. expand is not supported yet"); + } + if (this.listen_fds[fd] !== undefined) { + if (this.listen_fds[fd] instanceof Promise) { + console.warn("fd is already set yet"); + await this.listen_fds[fd]; + } + } + this.listen_fds[fd] = this.listen_fd(fd); + + const view = new Int32Array(this.fds_len_and_num); + Atomics.store(view, 0, this.fds.length); + // const len = Atomics.store(view, 0, this.fds.length); + // console.log("notify_set_fd: len: ", len); + } + + // abstract methods implementation + // called by fd close ex) fd_close + async notify_rm_fd(fd: number) { + (async () => { + await this.listen_fds[fd]; + (this.listen_fds as Array | undefined>)[fd] = undefined; + })(); + + // console.log("notify_rm_fd", fd); + // console.log("fds", this.fds); + // console.log("fds_map", this.fds_map); + + // console.log("notify_rm_fd: fds_map", this.fds_map); + // console.log("notify_rm_fd: fd", fd); + + // console.log("notify_rm_fd: fds_map[fd]", [...this.fds_map[fd]]); + + await this.fd_close_receiver.send(this.fds_map[fd], fd); + + this.fds_map[fd] = []; + } + + // abstract methods implementation + // wait to close old listener + can_set_new_fd(fd: number): [boolean, Promise | undefined] { + if (this.listen_fds[fd] instanceof Promise) { + return [false, this.listen_fds[fd]]; + } + return [true, undefined]; + } + + // listen all fds and base + // Must be called before was_ref_id is instantiated + listen() { + this.listen_fds = []; + for (let n = 0; n < this.fds.length; n++) { + this.listen_fds.push(this.listen_fd(n)); + } + this.listen_base_handle = this.listen_base(); + } + + // listen base + // ex) set_fds_map + // if close fd and send to other process, + // it need targets wasi_farm_ref id + // so, set fds_map + async listen_base() { + const lock_view = new Int32Array(this.base_func_util); + Atomics.store(lock_view, 0, 0); + Atomics.store(lock_view, 1, 0); + + // eslint-disable-next-line no-constant-condition + while (true) { + try { + let lock: "not-equal" | "timed-out" | "ok"; + + const { value } = Atomics.waitAsync(lock_view, 1, 0); + if (value instanceof Promise) { + lock = await value; + } else { + lock = value; + } + if (lock === "timed-out") { + throw new Error("timed-out"); + } + + const func_number = Atomics.load(lock_view, 2); + + switch (func_number) { + // set_fds_map: (fds_ptr: u32, fds_len: u32); + case 0: { + // console.log("set_fds_map"); + const ptr = Atomics.load(lock_view, 3); + const len = Atomics.load(lock_view, 4); + // console.log("set_fds_map", ptr, len); + const data = new Uint32Array(this.allocator.get_memory(ptr, len)); + this.allocator.free(ptr, len); + const wasi_farm_ref_id = Atomics.load(lock_view, 5); + + // console.log("listen_base set_fds_map", data, "from", wasi_farm_ref_id); + + // console.log("listen_base fds_map", this.fds_map); + + for (let i = 0; i < len / 4; i++) { + const fd = data[i]; + if (this.fds_map[fd] === undefined) { + this.fds_map[fd] = []; + console.error("listen_base fd is not defined"); + } + this.fds_map[fd].push(wasi_farm_ref_id); + // console.log("this.fds_map", this.fds_map); + // console.log("this.fds_map[fd]", this.fds_map[fd]); + // console.log("this.fds_map[1]", this.fds_map[1]); + // console.log("fd", fd, "wasi_farm_ref_id", wasi_farm_ref_id); + } + + // console.log("listen_base fds_map", this.fds_map); + + // sleep 1000ms + await new Promise((resolve) => setTimeout(resolve, 1000)); + + break; + } + } + + const old_call_lock = Atomics.exchange(lock_view, 1, 0); + if (old_call_lock !== 1) { + throw new Error("Lock is already set"); + } + const num = Atomics.notify(lock_view, 1, 1); + if (num !== 1) { + if (num === 0) { + console.warn("notify failed, waiter is late"); + continue; + } + throw new Error(`notify failed: ${num}`); + } + } catch (e) { + console.error("error", e); + Atomics.store(lock_view, 1, 0); + Atomics.notify(lock_view, 1, 1); + } + } + } + + // listen fd + async listen_fd(fd_n: number) { + const lock_view = new Int32Array(this.lock_fds, fd_n * 12); + const bytes_offset = fd_n * fd_func_sig_bytes; + const func_sig_view_u8 = new Uint8Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u16 = new Uint16Array(this.fd_func_sig, bytes_offset); + const func_sig_view_i32 = new Int32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u32 = new Int32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + const errno_offset = fd_func_sig_u32_size - 1; + Atomics.store(lock_view, 0, 0); + Atomics.store(lock_view, 1, 0); + Atomics.store(func_sig_view_i32, errno_offset, -1); + + // eslint-disable-next-line no-constant-condition + while (true) { + try { + let lock: "not-equal" | "timed-out" | "ok"; + + const { value } = Atomics.waitAsync(lock_view, 1, 0); + if (value instanceof Promise) { + // console.log("listen", fd_n, 1); + lock = await value; + } else { + lock = value; + } + if (lock === "timed-out") { + throw new Error("timed-out"); + } + + const func_lock = Atomics.load(lock_view, 1); + + if (func_lock !== 1) { + throw new Error(`func_lock is already set: ${func_lock}`); + } + + // console.log("func_lock", func_lock); + + // console.log("called", fd_n, 1); + + const set_error = (errno: number) => { + // console.log("set_error", errno, "pointer", errno_offset); + Atomics.store(func_sig_view_i32, errno_offset, errno); + }; + + const func_number = Atomics.load(func_sig_view_u32, 0); + + // console.log("called: func: ", get_func_name_from_number(func_number), "fd: ", fd_n); + + switch (func_number) { + // fd_advise: (fd: u32) => errno; + case 7: { + const fd = Atomics.load(func_sig_view_u32, 1); + + const error = this.fd_advise(fd); + + set_error(error); + break; + } + // fd_allocate: (fd: u32, offset: u64, len: u64) => errno; + case 8: { + const fd = Atomics.load(func_sig_view_u32, 1); + const offset = Atomics.load(func_sig_view_u64, 1); + const len = Atomics.load(func_sig_view_u64, 2); + + const error = this.fd_allocate(fd, offset, len); + + set_error(error); + break; + } + // fd_close: (fd: u32) => errno; + case 9: { + const fd = Atomics.load(func_sig_view_u32, 1); + + const error = await this.fd_close(fd); + + // console.log("fd_close", fd, error); + + set_error(error); + break; + } + // fd_datasync: (fd: u32) => errno; + case 10: { + const fd = Atomics.load(func_sig_view_u32, 1); + + const error = this.fd_datasync(fd); + + set_error(error); + break; + } + // fd_fdstat_get: (fd: u32) => [wasi.Fdstat(u32 * 6)], errno]; + case 11: { + const fd = Atomics.load(func_sig_view_u32, 1); + + const [fdstat, ret] = this.fd_fdstat_get(fd); + + if (fdstat) { + Atomics.store(func_sig_view_u8, 0, fdstat.fs_filetype); + Atomics.store(func_sig_view_u16, 2, fdstat.fs_flags); + Atomics.store(func_sig_view_u64, 1, fdstat.fs_rights_base); + Atomics.store(func_sig_view_u64, 2, fdstat.fs_rights_inherited); + } + set_error(ret); + break; + } + // fd_fdstat_set_flags: (fd: u32, flags: u16) => errno; + case 12: { + const fd = Atomics.load(func_sig_view_u32, 1); + const flags = Atomics.load(func_sig_view_u16, 4); + + const error = this.fd_fdstat_set_flags(fd, flags); + + set_error(error); + break; + } + // fd_fdstat_set_rights: (fd: u32, fs_rights_base: u64, fs_rights_inheriting: u64) => errno; + case 13: { + const fd = Atomics.load(func_sig_view_u32, 1); + const fs_rights_base = Atomics.load(func_sig_view_u64, 1); + const fs_rights_inheriting = Atomics.load(func_sig_view_u64, 2); + + const error = this.fd_fdstat_set_rights( + fd, + fs_rights_base, + fs_rights_inheriting, + ); + + set_error(error); + break; + } + // fd_filestat_get: (fd: u32) => [wasi.Filestat(u32 * 16)], errno]; + case 14: { + const fd = Atomics.load(func_sig_view_u32, 1); + + const [filestat, ret] = this.fd_filestat_get(fd); + + if (filestat) { + Atomics.store(func_sig_view_u64, 0, filestat.dev); + Atomics.store(func_sig_view_u64, 1, filestat.ino); + Atomics.store(func_sig_view_u8, 16, filestat.filetype); + Atomics.store(func_sig_view_u64, 3, filestat.nlink); + Atomics.store(func_sig_view_u64, 4, filestat.size); + Atomics.store(func_sig_view_u64, 5, filestat.atim); + Atomics.store(func_sig_view_u64, 6, filestat.mtim); + Atomics.store(func_sig_view_u64, 7, filestat.ctim); + } + + set_error(ret); + break; + } + // fd_filestat_set_size: (fd: u32, size: u64) => errno; + case 15: { + const fd = Atomics.load(func_sig_view_u32, 1); + const size = Atomics.load(func_sig_view_u64, 1); + + const error = this.fd_filestat_set_size(fd, size); + + set_error(error); + break; + } + // fd_filestat_set_times: (fd: u32, atim: u64, mtim: u64, fst_flags: u16) => errno; + case 16: { + const fd = Atomics.load(func_sig_view_u32, 1); + const atim = Atomics.load(func_sig_view_u64, 1); + const mtim = Atomics.load(func_sig_view_u64, 2); + const fst_flags = Atomics.load(func_sig_view_u16, 12); + + const error = this.fd_filestat_set_times(fd, atim, mtim, fst_flags); + + set_error(error); + break; + } + // fd_pread: (fd: u32, iovs_ptr: pointer, iovs_len: u32, offset: u64) => [u32, data_ptr, errno]; + case 17: { + const fd = Atomics.load(func_sig_view_u32, 1); + const iovs_ptr = Atomics.load(func_sig_view_u32, 2); + const iovs_ptr_len = Atomics.load(func_sig_view_u32, 3); + const offset = Atomics.load(func_sig_view_u64, 2); + const data = new Uint32Array( + this.allocator.get_memory(iovs_ptr, iovs_ptr_len), + ); + this.allocator.free(iovs_ptr, iovs_ptr_len); + + const iovecs = new Array(); + for (let i = 0; i < iovs_ptr_len; i += 8) { + const iovec = new wasi.Iovec(); + iovec.buf = data[i * 2]; + iovec.buf_len = data[i * 2 + 1]; + iovecs.push(iovec); + } + + // const [[nread, buffer8], error] = this.fd_pread(fd, iovecs, offset); + const [nread_and_buffer, error] = this.fd_pread(fd, iovecs, offset); + const [nread, buffer8] = nread_and_buffer ?? [undefined, undefined]; + + if (nread !== undefined) { + Atomics.store(func_sig_view_u32, 0, nread); + } + if (buffer8) { + await this.allocator.async_write( + buffer8, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 1, + ); + } + set_error(error); + break; + } + // fd_prestat_get: (fd: u32) => [wasi.Prestat(u32 * 2)], errno]; + case 18: { + const fd = Atomics.load(func_sig_view_u32, 1); + + const [prestat, ret] = this.fd_prestat_get(fd); + + // console.log("fd_prestat_get", prestat, ret); + + if (prestat) { + Atomics.store(func_sig_view_u32, 0, prestat.tag); + Atomics.store( + func_sig_view_u32, + 1, + prestat.inner.pr_name.byteLength, + ); + } + set_error(ret); + break; + } + // fd_prestat_dir_name: (fd: u32, path_len: u32) => [path_ptr: pointer, path_len: u32, errno]; + case 19: { + const fd = Atomics.load(func_sig_view_u32, 1); + const path_len = Atomics.load(func_sig_view_u32, 2); + + const [prestat_dir_name, ret] = this.fd_prestat_dir_name( + fd, + path_len, + ); + + // console.log("fd_prestat_dir_name: park: ", prestat_dir_name); + + if ( + prestat_dir_name && + (ret === wasi.ERRNO_SUCCESS || ret === wasi.ERRNO_NAMETOOLONG) + ) { + await this.allocator.async_write( + prestat_dir_name, + this.fd_func_sig, + fd * fd_func_sig_u32_size, + ); + } + set_error(ret); + break; + } + // fd_pwrite: (fd: u32, write_data: pointer, write_data_len: u32, offset: u64) => [u32, errno]; + case 20: { + const fd = Atomics.load(func_sig_view_u32, 1); + const write_data_ptr = Atomics.load(func_sig_view_u32, 2); + const write_data_len = Atomics.load(func_sig_view_u32, 3); + const offset = Atomics.load(func_sig_view_u64, 2); + + const data = new Uint8Array( + this.allocator.get_memory(write_data_ptr, write_data_len), + ); + this.allocator.free(write_data_ptr, write_data_len); + + const [nwritten, error] = this.fd_pwrite(fd, data, offset); + + if (nwritten !== undefined) { + Atomics.store(func_sig_view_u32, 0, nwritten); + } + set_error(error); + break; + } + // fd_read: (fd: u32, iovs_ptr: pointer, iovs_len: u32) => [u32, data_ptr, errno]; + case 21: { + const fd = Atomics.load(func_sig_view_u32, 1); + const iovs_ptr = Atomics.load(func_sig_view_u32, 2); + const iovs_ptr_len = Atomics.load(func_sig_view_u32, 3); + // console.log("fd_read: park: iovs: Uint8Array", this.allocator.get_memory(iovs_ptr, iovs_ptr_len)); + // console.log("ptr_len", iovs_ptr_len); + const iovs = new Uint32Array( + this.allocator.get_memory(iovs_ptr, iovs_ptr_len), + ); + this.allocator.free(iovs_ptr, iovs_ptr_len); + + // console.log("fd_read: park: iovs", iovs); + + const iovecs = new Array(); + for (let i = 0; i < iovs_ptr_len; i += 8) { + const iovec = new wasi.Iovec(); + iovec.buf = iovs[i * 2]; + iovec.buf_len = iovs[i * 2 + 1]; + iovecs.push(iovec); + } + + // console.log("fd_read: park: iovecs", iovecs); + + const [nread_and_buffer, error] = this.fd_read(fd, iovecs); + const [nread, buffer8] = nread_and_buffer ?? [undefined, undefined]; + + // console.log("fd_read: park: buffer8", new TextDecoder().decode(buffer8)); + + if (nread !== undefined) { + Atomics.store(func_sig_view_u32, 0, nread); + } + if (buffer8) { + await this.allocator.async_write( + buffer8, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 1, + ); + } + set_error(error); + break; + } + // fd_readdir: (fd: u32, buf_len: u32, cookie: u64) => [buf_ptr: pointer, buf_len: u32, buf_used: u32, errno]; + case 22: { + const fd = Atomics.load(func_sig_view_u32, 1); + const buf_len = Atomics.load(func_sig_view_u32, 2); + const cookie = Atomics.load(func_sig_view_u64, 2); + + const [array_and_buf_used, error] = this.fd_readdir( + fd, + buf_len, + cookie, + ); + const [array, buf_used] = array_and_buf_used ?? [ + undefined, + undefined, + ]; + + if (array) { + await this.allocator.async_write( + array, + this.fd_func_sig, + fd * fd_func_sig_u32_size, + ); + } + if (buf_used !== undefined) { + Atomics.store(func_sig_view_u32, 2, buf_used); + } + set_error(error); + break; + } + // fd_seek: (fd: u32, offset: i64, whence: u8) => [u64, errno]; + case 24: { + const fd = Atomics.load(func_sig_view_u32, 1); + const offset = Atomics.load(func_sig_view_u64, 1); + const whence = Atomics.load(func_sig_view_u8, 16); + + const [new_offset, error] = this.fd_seek(fd, offset, whence); + + if (new_offset !== undefined) { + Atomics.store(func_sig_view_u64, 0, new_offset); + } + set_error(error); + break; + } + // fd_sync: (fd: u32) => errno; + case 25: { + const fd = Atomics.load(func_sig_view_u32, 1); + + const error = this.fd_sync(fd); + + set_error(error); + break; + } + // fd_tell: (fd: u32) => [u64, errno]; + case 26: { + const fd = Atomics.load(func_sig_view_u32, 1); + + const [offset, error] = this.fd_tell(fd); + + if (offset !== undefined) { + Atomics.store(func_sig_view_u64, 0, offset); + } + set_error(error); + break; + } + // fd_write: (fd: u32, write_data: pointer, write_data_len: u32) => [u32, errno]; + case 27: { + const fd = Atomics.load(func_sig_view_u32, 1); + const write_data_ptr = Atomics.load(func_sig_view_u32, 2); + const write_data_len = Atomics.load(func_sig_view_u32, 3); + + const data = new Uint8Array( + this.allocator.get_memory(write_data_ptr, write_data_len), + ); + this.allocator.free(write_data_ptr, write_data_len); + + // console.log("allocator", this.allocator); + + // console.log("fd_write: park: write_data", new TextDecoder().decode(data)); + + const [nwritten, error] = await this.fd_write(fd, data); + + // console.log("fd_write: park: error", error); + + if (nwritten !== undefined) { + Atomics.store(func_sig_view_u32, 0, nwritten); + } + set_error(error); + break; + } + // path_create_directory: (fd: u32, path_ptr: pointer, path_len: u32) => errno; + case 28: { + const fd = Atomics.load(func_sig_view_u32, 1); + const path_ptr = Atomics.load(func_sig_view_u32, 2); + const path_len = Atomics.load(func_sig_view_u32, 3); + + const path = new Uint8Array( + this.allocator.get_memory(path_ptr, path_len), + ); + const path_str = new TextDecoder().decode(path); + this.allocator.free(path_ptr, path_len); + + const error = this.path_create_directory(fd, path_str); + + set_error(error); + break; + } + // path_filestat_get: (fd: u32, flags: u32, path_ptr: pointer, path_len: u32) => [wasi.Filestat(u32 * 16), errno]; + case 29: { + const fd = Atomics.load(func_sig_view_u32, 1); + const flags = Atomics.load(func_sig_view_u32, 2); + const path_ptr = Atomics.load(func_sig_view_u32, 3); + const path_len = Atomics.load(func_sig_view_u32, 4); + + const path = new Uint8Array( + this.allocator.get_memory(path_ptr, path_len), + ); + const path_str = new TextDecoder().decode(path); + this.allocator.free(path_ptr, path_len); + + const [filestat, ret] = this.path_filestat_get(fd, flags, path_str); + + if (filestat) { + Atomics.store(func_sig_view_u64, 0, filestat.dev); + Atomics.store(func_sig_view_u64, 1, filestat.ino); + Atomics.store(func_sig_view_u8, 16, filestat.filetype); + Atomics.store(func_sig_view_u64, 3, filestat.nlink); + Atomics.store(func_sig_view_u64, 4, filestat.size); + Atomics.store(func_sig_view_u64, 5, filestat.atim); + Atomics.store(func_sig_view_u64, 6, filestat.mtim); + Atomics.store(func_sig_view_u64, 7, filestat.ctim); + } + set_error(ret); + break; + } + // path_filestat_set_times: (fd: u32, flags: u32, path_ptr: pointer, path_len: u32, atim: u64, mtim: u64, fst_flags: u16) => errno; + case 30: { + const fd = Atomics.load(func_sig_view_u32, 1); + const flags = Atomics.load(func_sig_view_u32, 2); + const path_ptr = Atomics.load(func_sig_view_u32, 3); + const path_len = Atomics.load(func_sig_view_u32, 4); + const atim = Atomics.load(func_sig_view_u64, 3); + const mtim = Atomics.load(func_sig_view_u64, 4); + const fst_flags = Atomics.load(func_sig_view_u16, 12); + + const path = new Uint8Array( + this.allocator.get_memory(path_ptr, path_len), + ); + const path_str = new TextDecoder().decode(path); + this.allocator.free(path_ptr, path_len); + + const error = this.path_filestat_set_times( + fd, + flags, + path_str, + atim, + mtim, + fst_flags, + ); + + set_error(error); + break; + } + // path_link: (old_fd: u32, old_flags: u32, old_path_ptr: pointer, old_path_len: u32, new_fd: u32, new_path_ptr: pointer, new_path_len: u32) => errno; + case 31: { + const old_fd = Atomics.load(func_sig_view_u32, 1); + const old_flags = Atomics.load(func_sig_view_u32, 2); + const old_path_ptr = Atomics.load(func_sig_view_u32, 3); + const old_path_len = Atomics.load(func_sig_view_u32, 4); + const new_fd = Atomics.load(func_sig_view_u32, 5); + const new_path_ptr = Atomics.load(func_sig_view_u32, 6); + const new_path_len = Atomics.load(func_sig_view_u32, 7); + + const old_path = new Uint8Array( + this.allocator.get_memory(old_path_ptr, old_path_len), + ); + const old_path_str = new TextDecoder().decode(old_path); + this.allocator.free(old_path_ptr, old_path_len); + const new_path = new Uint8Array( + this.allocator.get_memory(new_path_ptr, new_path_len), + ); + const new_path_str = new TextDecoder().decode(new_path); + this.allocator.free(new_path_ptr, new_path_len); + + const error = this.path_link( + old_fd, + old_flags, + old_path_str, + new_fd, + new_path_str, + ); + + set_error(error); + break; + } + // path_open: (fd: u32, dirflags: u32, path_ptr: pointer, path_len: u32, oflags: u32, fs_rights_base: u64, fs_rights_inheriting: u64, fdflags: u16) => [u32, errno]; + case 32: { + const fd = Atomics.load(func_sig_view_u32, 1); + const dirflags = Atomics.load(func_sig_view_u32, 2); + const path_ptr = Atomics.load(func_sig_view_u32, 3); + const path_len = Atomics.load(func_sig_view_u32, 4); + const oflags = Atomics.load(func_sig_view_u32, 5); + const fs_rights_base = Atomics.load(func_sig_view_u64, 3); + const fs_rights_inheriting = Atomics.load(func_sig_view_u64, 4); + const fd_flags = Atomics.load(func_sig_view_u16, 20); + + const path = new Uint8Array( + this.allocator.get_memory(path_ptr, path_len), + ); + const path_str = new TextDecoder().decode(path); + this.allocator.free(path_ptr, path_len); + + const [opened_fd, error] = await this.path_open( + fd, + dirflags, + path_str, + oflags, + fs_rights_base, + fs_rights_inheriting, + fd_flags, + ); + + // console.log("path_open: opend_fd", opened_fd, error); + + if (opened_fd !== undefined) { + Atomics.store(func_sig_view_u32, 0, opened_fd); + } + set_error(error); + break; + } + // path_readlink: (fd: u32, path_ptr: pointer, path_len: u32, buf_len: u32) => [buf_len: u32, data_ptr: pointer, data_len: u32, errno]; + case 33: { + const fd = Atomics.load(func_sig_view_u32, 1); + const path_ptr = Atomics.load(func_sig_view_u32, 2); + const path_len = Atomics.load(func_sig_view_u32, 3); + const buf_len = Atomics.load(func_sig_view_u32, 4); + + const path = new Uint8Array( + this.allocator.get_memory(path_ptr, path_len), + ); + const path_str = new TextDecoder().decode(path); + this.allocator.free(path_ptr, path_len); + + const [buf, error] = this.path_readlink(fd, path_str, buf_len); + + if (buf) { + await this.allocator.async_write( + buf, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 1, + ); + Atomics.store(func_sig_view_u32, 0, buf.byteLength); + } + set_error(error); + break; + } + // path_remove_directory: (fd: u32, path_ptr: pointer, path_len: u32) => errno; + case 34: { + const fd = Atomics.load(func_sig_view_u32, 1); + const path_ptr = Atomics.load(func_sig_view_u32, 2); + const path_len = Atomics.load(func_sig_view_u32, 3); + + const path = new Uint8Array( + this.allocator.get_memory(path_ptr, path_len), + ); + const path_str = new TextDecoder().decode(path); + this.allocator.free(path_ptr, path_len); + + const error = this.path_remove_directory(fd, path_str); + + set_error(error); + break; + } + // path_rename: (old_fd: u32, old_path_ptr: pointer, old_path_len: u32, new_fd: u32, new_path_ptr: pointer, new_path_len: u32) => errno; + case 35: { + const fd = Atomics.load(func_sig_view_u32, 1); + const old_path_ptr = Atomics.load(func_sig_view_u32, 2); + const old_path_len = Atomics.load(func_sig_view_u32, 3); + const new_fd = Atomics.load(func_sig_view_u32, 4); + const new_path_ptr = Atomics.load(func_sig_view_u32, 5); + const new_path_len = Atomics.load(func_sig_view_u32, 6); + + const old_path = new Uint8Array( + this.allocator.get_memory(old_path_ptr, old_path_len), + ); + const old_path_str = new TextDecoder().decode(old_path); + this.allocator.free(old_path_ptr, old_path_len); + const new_path = new Uint8Array( + this.allocator.get_memory(new_path_ptr, new_path_len), + ); + const new_path_str = new TextDecoder().decode(new_path); + this.allocator.free(new_path_ptr, new_path_len); + + const error = this.path_rename( + fd, + old_path_str, + new_fd, + new_path_str, + ); + + set_error(error); + break; + } + // path_symlink: (old_path_ptr: pointer, old_path_len: u32, fd: u32, new_path_ptr: pointer, new_path_len: u32) => errno; + case 36: { + const old_path_ptr = Atomics.load(func_sig_view_u32, 1); + const old_path_len = Atomics.load(func_sig_view_u32, 2); + const fd = Atomics.load(func_sig_view_u32, 3); + const new_path_ptr = Atomics.load(func_sig_view_u32, 4); + const new_path_len = Atomics.load(func_sig_view_u32, 5); + + const old_path = new Uint8Array( + this.allocator.get_memory(old_path_ptr, old_path_len), + ); + const old_path_str = new TextDecoder().decode(old_path); + this.allocator.free(old_path_ptr, old_path_len); + const new_path = new Uint8Array( + this.allocator.get_memory(new_path_ptr, new_path_len), + ); + const new_path_str = new TextDecoder().decode(new_path); + this.allocator.free(new_path_ptr, new_path_len); + + set_error(this.path_symlink(old_path_str, fd, new_path_str)); + break; + } + // path_unlink_file: (fd: u32, path_ptr: pointer, path_len: u32) => errno; + case 37: { + const fd = Atomics.load(func_sig_view_u32, 1); + const path_ptr = Atomics.load(func_sig_view_u32, 2); + const path_len = Atomics.load(func_sig_view_u32, 3); + + const path = new Uint8Array( + this.allocator.get_memory(path_ptr, path_len), + ); + const path_str = new TextDecoder().decode(path); + this.allocator.free(path_ptr, path_len); + + set_error(this.path_unlink_file(fd, path_str)); + break; + } + default: { + throw new Error(`Unknown function number: ${func_number}`); + } + } + + const old_call_lock = Atomics.exchange(lock_view, 1, 0); + if (old_call_lock !== 1) { + throw new Error( + `Call is already set: ${old_call_lock}\nfunc: ${get_func_name_from_number(func_number)}\nfd: ${fd_n}`, + ); + } + + // console.log("called end: func: ", get_func_name_from_number(func_number), "fd: ", fd_n); + + const n = Atomics.notify(lock_view, 1); + if (n !== 1) { + if (n === 0) { + console.warn("notify number is 0. ref is late?"); + } else { + console.warn(`notify number is not 1: ${n}`); + } + } + + if (this.fds[fd_n] === undefined) { + break; + } + } catch (e) { + console.error(e); + + // sleep 1000ms + await new Promise((resolve) => setTimeout(resolve, 1000)); + + const lock_view = new Int32Array(this.lock_fds); + Atomics.exchange(lock_view, 1, 0); + const func_sig_view = new Int32Array(this.fd_func_sig); + Atomics.exchange(func_sig_view, 16, -1); + } + } + } +} diff --git a/threads/src/shared_array_buffer/ref.ts b/threads/src/shared_array_buffer/ref.ts new file mode 100644 index 0000000..55d6503 --- /dev/null +++ b/threads/src/shared_array_buffer/ref.ts @@ -0,0 +1,1411 @@ +import { wasi } from "@bjorn3/browser_wasi_shim"; +import { WASIFarmRef, type WASIFarmRefObject } from "../ref.js"; +import { + AllocatorUseArrayBuffer, + type AllocatorUseArrayBufferObject, +} from "./allocator.js"; +import { + FdCloseSenderUseArrayBuffer, + type FdCloseSenderUseArrayBufferObject, +} from "./fd_close_sender.js"; +import { fd_func_sig_bytes, fd_func_sig_u32_size } from "./park.js"; + +export type WASIFarmRefUseArrayBufferObject = { + allocator: AllocatorUseArrayBuffer; + lock_fds: SharedArrayBuffer; + fds_len_and_num: SharedArrayBuffer; + fd_func_sig: SharedArrayBuffer; + base_func_util: SharedArrayBuffer; +} & WASIFarmRefObject; + +// Transmittable objects to communicate with Park +export class WASIFarmRefUseArrayBuffer extends WASIFarmRef { + // For more information on member variables, see . See /park.ts + allocator: AllocatorUseArrayBuffer; + lock_fds: SharedArrayBuffer; + // byte 1: fds_len + // byte 2: all wasi_farm_ref num + fds_len_and_num: SharedArrayBuffer; + fd_func_sig: SharedArrayBuffer; + base_func_util: SharedArrayBuffer; + + constructor( + allocator: AllocatorUseArrayBufferObject, + lock_fds: SharedArrayBuffer, + fds_len_and_num: SharedArrayBuffer, + fd_func_sig: SharedArrayBuffer, + base_func_util: SharedArrayBuffer, + fd_close_receiver: FdCloseSenderUseArrayBufferObject, + stdin: number | undefined, + stdout: number | undefined, + stderr: number | undefined, + default_fds: Array, + ) { + super( + stdin, + stdout, + stderr, + FdCloseSenderUseArrayBuffer.init_self(fd_close_receiver), + default_fds, + ); + this.allocator = AllocatorUseArrayBuffer.init_self(allocator); + this.lock_fds = lock_fds; + this.fd_func_sig = fd_func_sig; + this.base_func_util = base_func_util; + this.fds_len_and_num = fds_len_and_num; + + // console.log("fds_len_and_num", this.fds_len_and_num); + + // const view = new Int32Array(this.fds_len_and_num); + // Atomics.store(view, 0, 0); + } + + get_fds_len(): number { + const view = new Int32Array(this.fds_len_and_num); + return Atomics.load(view, 0); + } + + static init_self(sl: WASIFarmRefUseArrayBufferObject): WASIFarmRef { + return new WASIFarmRefUseArrayBuffer( + sl.allocator, + sl.lock_fds, + sl.fds_len_and_num, + sl.fd_func_sig, + sl.base_func_util, + sl.fd_close_receiver as unknown as FdCloseSenderUseArrayBufferObject, + sl.stdin, + sl.stdout, + sl.stderr, + sl.default_fds, + ); + } + + // allocate a new id on wasi_farm_ref and return it + set_id(): number { + const view = new Int32Array(this.fds_len_and_num); + const id = Atomics.add(view, 1, 1); + this.id = id; + return id; + } + + // lock base_func + private lock_base_func(): void { + const view = new Int32Array(this.base_func_util); + // eslint-disable-next-line no-constant-condition + while (true) { + const lock = Atomics.wait(view, 0, 1); + if (lock === "timed-out") { + throw new Error("timed-out lock"); + } + const old = Atomics.compareExchange(view, 0, 0, 1); + if (old !== 0) { + continue; + } + break; + } + } + + // call base_func + private call_base_func(): void { + const view = new Int32Array(this.base_func_util); + const old = Atomics.exchange(view, 1, 1); + if (old !== 0) { + console.error("what happened?"); + } + Atomics.notify(view, 1, 1); + } + + // wait base_func + private wait_base_func(): void { + const view = new Int32Array(this.base_func_util); + const lock = Atomics.wait(view, 1, 1); + if (lock === "timed-out") { + throw new Error("timed-out lock"); + } + } + + // release base_func + private release_base_func(): void { + const view = new Int32Array(this.base_func_util); + Atomics.store(view, 0, 0); + Atomics.notify(view, 0, 1); + } + + // set park_fds_map + set_park_fds_map(fds: Array): void { + this.lock_base_func(); + const view = new Int32Array(this.base_func_util); + Atomics.store(view, 2, 0); + const fds_array = new Uint32Array(fds); + // console.log("fds_array", fds_array); + this.allocator.block_write(fds_array, this.base_func_util, 3); + Atomics.store(view, 5, this.id); + this.call_base_func(); + this.wait_base_func(); + this.release_base_func(); + } + + private lock_fd(fd: number) { + // console.log("lock_fd start", fd); + const view = new Int32Array(this.lock_fds, fd * 12); + // eslint-disable-next-line no-constant-condition + while (true) { + const now_value = Atomics.load(view, 0); + if (now_value !== 0) { + const value = Atomics.wait(view, 0, now_value); + if (value === "timed-out") { + console.error("lock_fd timed-out"); + continue; + } + } + const old = Atomics.compareExchange(view, 0, 0, 1); + if (old === 0) { + // console.log("lock_fd success", fd); + return; + } + } + } + + private release_fd(fd: number) { + // console.log("release_fd", fd); + const view = new Int32Array(this.lock_fds, fd * 12); + Atomics.store(view, 0, 0); + Atomics.notify(view, 0, 1); + } + + private lock_double_fd(fd1: number, fd2: number) { + // console.log("lock_double_fd", fd1, fd2); + if (fd1 === fd2) { + this.lock_fd(fd1); + return; + } + const view = new Int32Array(this.lock_fds); + // eslint-disable-next-line no-constant-condition + while (true) { + const now_value1 = Atomics.load(view, fd1 * 3); + const value = Atomics.wait(view, fd1 * 3, now_value1); + if (value === "timed-out") { + console.error("lock_double_fd timed-out"); + continue; + } + const old1 = Atomics.exchange(view, fd1 * 3, 2); + if (old1 === 0) { + const now_value2 = Atomics.load(view, fd2 * 3); + if (now_value2 === 2) { + // It's nearly deadlocked. + if (fd1 < fd2) { + // release fd1 + Atomics.store(view, fd1 * 3, 0); + Atomics.notify(view, fd1 * 3, 1); + continue; + } + } + const value = Atomics.wait(view, fd2 * 3, now_value2); + if (value === "timed-out") { + console.error("lock_double_fd timed-out"); + continue; + } + const old2 = Atomics.exchange(view, fd2 * 3, 2); + if (old2 === 0) { + return; + } + Atomics.store(view, fd1 * 3, 0); + Atomics.notify(view, fd1 * 3, 1); + } + } + } + + private release_double_fd(fd1: number, fd2: number) { + // console.log("release_double_fd", fd1, fd2); + if (fd1 === fd2) { + this.release_fd(fd1); + return; + } + const view = new Int32Array(this.lock_fds); + Atomics.store(view, fd1 * 3, 0); + Atomics.notify(view, fd1 * 3, 1); + Atomics.store(view, fd2 * 3, 0); + Atomics.notify(view, fd2 * 3, 1); + } + + private invoke_fd_func(fd: number): boolean { + if (fd === undefined) { + return false; + } + // console.log("invoke_fd_func", fd); + const view = new Int32Array(this.lock_fds, fd * 12 + 4); + const old = Atomics.exchange(view, 0, 1); + if (old === 1) { + console.error(`invoke_fd_func already invoked\nfd: ${fd}`); + return false; + } + const n = Atomics.notify(view, 0); + if (n !== 1) { + if (n === 0) { + const len = this.get_fds_len(); + if (len <= fd) { + const lock = Atomics.exchange(view, 0, 0); + if (lock !== 1) { + console.error("what happened?"); + } + Atomics.notify(view, 0, 1); + console.error("what happened?: len", len, "fd", fd); + return true; + } + console.warn("invoke_func_loop is late"); + return true; + } + console.error("invoke_fd_func notify failed:", n); + return false; + } + return true; + } + + private wait_fd_func(fd: number) { + // console.log("wait_fd_func", fd); + const view = new Int32Array(this.lock_fds, fd * 12 + 4); + const value = Atomics.wait(view, 0, 1); + if (value === "timed-out") { + console.error("wait call park_fd_func timed-out"); + } + } + + private call_fd_func(fd: number): boolean { + if (!this.invoke_fd_func(fd)) { + return false; + } + // console.log("call_fd_func", fd); + this.wait_fd_func(fd); + // console.log("wait_fd_func", fd); + // console.log("call_fd_func released", fd); + return true; + } + + private get_error(fd: number): number { + const func_sig_view_i32 = new Int32Array( + this.fd_func_sig, + fd * fd_func_sig_bytes, + ); + const errno_offset = fd_func_sig_u32_size - 1; + // console.log("get_error: offset", errno_offset); + return Atomics.load(func_sig_view_i32, errno_offset); + } + + fd_advise(fd: number): number { + this.lock_fd(fd); + + const func_sig_view_u32 = new Uint32Array( + this.fd_func_sig, + fd * fd_func_sig_bytes, + ); + + Atomics.store(func_sig_view_u32, 0, 7); + Atomics.store(func_sig_view_u32, 1, fd); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + fd_allocate(fd: number, offset: bigint, len: bigint): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 8); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u64, 1, offset); + Atomics.store(func_sig_view_u64, 2, len); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + fd_close(fd: number): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 9); + Atomics.store(func_sig_view_u32, 1, fd); + + // console.log("fd_close: ref", fd); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + // console.log("fd_close: ref2", fd); + + const error = this.get_error(fd); + + this.release_fd(fd); + + // console.log("fd_close: ref3", fd); + + return error; + } + + fd_datasync(fd: number): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 10); + Atomics.store(func_sig_view_u32, 1, fd); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + fd_fdstat_get(fd: number): [wasi.Fdstat | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u8 = new Uint8Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u16 = new Uint16Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 11); + Atomics.store(func_sig_view_u32, 1, fd); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + if (error !== wasi.ERRNO_SUCCESS) { + this.release_fd(fd); + return [undefined, error]; + } + + const fs_filetype = Atomics.load(func_sig_view_u8, 0); + const fs_flags = Atomics.load(func_sig_view_u16, 2); + const fs_rights_base = Atomics.load(func_sig_view_u64, 1); + const fs_rights_inheriting = Atomics.load(func_sig_view_u64, 2); + + this.release_fd(fd); + + const fd_stat = new wasi.Fdstat(fs_filetype, fs_flags); + fd_stat.fs_rights_base = fs_rights_base; + fd_stat.fs_rights_inherited = fs_rights_inheriting; + + return [fd_stat, error]; + } + + fd_fdstat_set_flags(fd: number, flags: number): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u16 = new Uint16Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 12); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u16, 4, flags); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + fd_fdstat_set_rights( + fd: number, + fs_rights_base: bigint, + fs_rights_inheriting: bigint, + ): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 13); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u64, 1, fs_rights_base); + Atomics.store(func_sig_view_u64, 2, fs_rights_inheriting); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + fd_filestat_get(fd: number): [wasi.Filestat | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u8 = new Uint8Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 14); + Atomics.store(func_sig_view_u32, 1, fd); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + if (error !== wasi.ERRNO_SUCCESS) { + this.release_fd(fd); + return [undefined, error]; + } + + const fs_dev = Atomics.load(func_sig_view_u64, 0); + const fs_ino = Atomics.load(func_sig_view_u64, 1); + const fs_filetype = Atomics.load(func_sig_view_u8, 16); + const fs_nlink = Atomics.load(func_sig_view_u64, 3); + const fs_size = Atomics.load(func_sig_view_u64, 4); + const fs_atim = Atomics.load(func_sig_view_u64, 5); + const fs_mtim = Atomics.load(func_sig_view_u64, 6); + const fs_ctim = Atomics.load(func_sig_view_u64, 7); + + this.release_fd(fd); + + const file_stat = new wasi.Filestat(fs_filetype, fs_size); + file_stat.dev = fs_dev; + file_stat.ino = fs_ino; + file_stat.nlink = fs_nlink; + file_stat.atim = fs_atim; + file_stat.mtim = fs_mtim; + file_stat.ctim = fs_ctim; + + return [file_stat, error]; + } + + fd_filestat_set_size(fd: number, size: bigint): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 15); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u64, 1, size); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + fd_filestat_set_times( + fd: number, + st_atim: bigint, + st_mtim: bigint, + fst_flags: number, + ): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u16 = new Uint16Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 16); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u64, 1, st_atim); + Atomics.store(func_sig_view_u64, 2, st_mtim); + Atomics.store(func_sig_view_u16, 12, fst_flags); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + fd_pread( + fd: number, + iovs: Uint32Array, + offset: bigint, + ): [[number, Uint8Array] | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 17); + Atomics.store(func_sig_view_u32, 1, fd); + const [ptr, len] = this.allocator.block_write( + iovs, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 2, + ); + Atomics.store(func_sig_view_u64, 2, offset); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr, len); + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + const nread = Atomics.load(func_sig_view_u32, 0); + const buf_ptr = Atomics.load(func_sig_view_u32, 1); + const buf_len = Atomics.load(func_sig_view_u32, 2); + this.release_fd(fd); + + if (error === wasi.ERRNO_BADF) { + this.allocator.free(buf_ptr, buf_len); + return [undefined, error]; + } + + const buf = new Uint8Array(this.allocator.get_memory(buf_ptr, buf_len)); + + if (nread !== buf_len) { + console.error("pread nread !== buf_len"); + } + + this.allocator.free(buf_ptr, buf_len); + + return [[nread, buf], error]; + } + + fd_prestat_get(fd: number): [[number, number] | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 18); + Atomics.store(func_sig_view_u32, 1, fd); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + if (error !== wasi.ERRNO_SUCCESS) { + this.release_fd(fd); + return [undefined, error]; + } + + const pr_tag = Atomics.load(func_sig_view_u32, 0); + const pr_name_len = Atomics.load(func_sig_view_u32, 1); + + this.release_fd(fd); + + return [[pr_tag, pr_name_len], error]; + } + + fd_prestat_dir_name( + fd: number, + path_len: number, + ): [Uint8Array | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 19); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u32, 2, path_len); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + const ret_path_ptr = Atomics.load(func_sig_view_u32, 0); + const ret_path_len = Atomics.load(func_sig_view_u32, 1); + + this.release_fd(fd); + if (error !== wasi.ERRNO_SUCCESS && error !== wasi.ERRNO_NAMETOOLONG) { + this.allocator.free(ret_path_ptr, ret_path_len); + return [undefined, error]; + } + + const ret_path = new Uint8Array( + this.allocator.get_memory(ret_path_ptr, ret_path_len), + ); + this.allocator.free(ret_path_ptr, ret_path_len); + + return [ret_path, error]; + } + + fd_pwrite( + fd: number, + write_data: Uint8Array, + offset: bigint, + ): [number | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 20); + Atomics.store(func_sig_view_u32, 1, fd); + const [ptr, len] = this.allocator.block_write( + write_data, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 2, + ); + Atomics.store(func_sig_view_u64, 2, offset); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr, len); + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + if (error === wasi.ERRNO_BADF) { + this.release_fd(fd); + return [undefined, error]; + } + + const nwritten = Atomics.load(func_sig_view_u32, 0); + + this.release_fd(fd); + + return [nwritten, error]; + } + + fd_read( + fd: number, + iovs: Uint32Array, + ): [[number, Uint8Array] | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 21); + Atomics.store(func_sig_view_u32, 1, fd); + + // console.log("fd_read: ref: iovs", iovs); + // console.log("iovs.buffer", iovs.buffer.slice(0, iovs.byteLength)); + + const [ptr, len] = this.allocator.block_write( + iovs, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 2, + ); + + // console.log("fd_read: ref: iovs", iovs); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr, len); + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + const nread = Atomics.load(func_sig_view_u32, 0); + const buf_ptr = Atomics.load(func_sig_view_u32, 1); + const buf_len = Atomics.load(func_sig_view_u32, 2); + this.release_fd(fd); + + // console.log("fd_read: ref: ", nread, buf_ptr, buf_len); + + if (error === wasi.ERRNO_BADF) { + this.allocator.free(buf_ptr, buf_len); + return [undefined, error]; + } + + // fd_read: ref: 14 30 14 + // animals.ts:325 fd_read: nread 14 Hello, world! + // fd_read: ref: 21 52 32 + // ref.ts:655 fd_read: ref: 21 + const buf = new Uint8Array(this.allocator.get_memory(buf_ptr, buf_len)); + // console.log("fd_read: ref: ", nread, new TextDecoder().decode(buf)); + + // console.log("fd_read: nread", nread, new TextDecoder().decode(buf)); + + if (nread !== buf_len) { + console.error("read nread !== buf_len"); + } + + this.allocator.free(buf_ptr, buf_len); + + return [[nread, buf], error]; + } + + fd_readdir( + fd: number, + limit_buf_len: number, + cookie: bigint, + ): [[Uint8Array, number] | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 22); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u32, 2, limit_buf_len); + Atomics.store(func_sig_view_u64, 2, cookie); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + const buf_ptr = Atomics.load(func_sig_view_u32, 0); + const buf_len = Atomics.load(func_sig_view_u32, 1); + const buf_used = Atomics.load(func_sig_view_u32, 2); + this.release_fd(fd); + + if (error === wasi.ERRNO_BADF) { + this.allocator.free(buf_ptr, buf_len); + return [undefined, error]; + } + + const buf = new Uint8Array(this.allocator.get_memory(buf_ptr, buf_len)); + + this.allocator.free(buf_ptr, buf_len); + + return [[buf, buf_used], error]; + } + + // fd_renumber( + // fd: number, + // to: number, + // ): number { + // this.lock_double_fd(fd, to); + + // const bytes_offset = fd * fd_func_sig_bytes; + // const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + // // fd + // Atomics.store(func_sig_view_u32, 0, 23); + // Atomics.store(func_sig_view_u32, 1, fd); + // Atomics.store(func_sig_view_u32, 2, to); + + // if (!this.call_fd_func(fd)) { + // this.release_fd(fd); + // return wasi.ERRNO_BADF; + // } + + // const error = this.get_error(fd); + + // this.release_double_fd(fd, to); + + // return error; + // } + + fd_seek( + fd: number, + offset: bigint, + whence: number, + ): [bigint | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u8 = new Uint8Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 24); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u64, 1, offset); + Atomics.store(func_sig_view_u8, 16, whence); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + if (error === wasi.ERRNO_BADF) { + this.release_fd(fd); + return [undefined, error]; + } + + const new_offset = Atomics.load(func_sig_view_u64, 0); + + this.release_fd(fd); + + return [new_offset, error]; + } + + fd_sync(fd: number): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 25); + Atomics.store(func_sig_view_u32, 1, fd); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + fd_tell(fd: number): [bigint | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 26); + Atomics.store(func_sig_view_u32, 1, fd); + + if (!this.call_fd_func(fd)) { + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + if (error === wasi.ERRNO_BADF) { + this.release_fd(fd); + return [undefined, error]; + } + + const offset = Atomics.load(func_sig_view_u64, 0); + + this.release_fd(fd); + + return [offset, error]; + } + + fd_write(fd: number, write_data: Uint8Array): [number | undefined, number] { + this.lock_fd(fd); + + // console.log("fd_write: ref: write_data", new TextDecoder().decode(write_data)); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 27); + Atomics.store(func_sig_view_u32, 1, fd); + const [ptr, len] = this.allocator.block_write( + write_data, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 2, + ); + + if (!this.call_fd_func(fd)) { + // console.log("fd_write: ref: error", "wasi.ERRNO_BADF"); + + this.allocator.free(ptr, len); + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + // console.log("fd_write: ref: error", this.get_error(fd)); + + // console.log("fd_write: ref: error", error); + + if (error === wasi.ERRNO_BADF) { + this.release_fd(fd); + return [undefined, error]; + } + + const nwritten = Atomics.load(func_sig_view_u32, 0); + + this.release_fd(fd); + + return [nwritten, error]; + } + + path_create_directory(fd: number, path: Uint8Array): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 28); + Atomics.store(func_sig_view_u32, 1, fd); + const [ptr, len] = this.allocator.block_write( + path, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 2, + ); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr, len); + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + path_filestat_get( + fd: number, + flags: number, + path: Uint8Array, + ): [wasi.Filestat | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u8 = new Uint8Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 29); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u32, 2, flags); + const [ptr, len] = this.allocator.block_write( + path, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 3, + ); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr, len); + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + if (error !== wasi.ERRNO_SUCCESS) { + this.release_fd(fd); + return [undefined, error]; + } + + const fs_dev = Atomics.load(func_sig_view_u64, 0); + const fs_ino = Atomics.load(func_sig_view_u64, 1); + const fs_filetype = Atomics.load(func_sig_view_u8, 16); + const fs_nlink = Atomics.load(func_sig_view_u64, 3); + const fs_size = Atomics.load(func_sig_view_u64, 4); + const fs_atim = Atomics.load(func_sig_view_u64, 5); + const fs_mtim = Atomics.load(func_sig_view_u64, 6); + const fs_ctim = Atomics.load(func_sig_view_u64, 7); + + this.release_fd(fd); + + const file_stat = new wasi.Filestat(fs_filetype, fs_size); + file_stat.dev = fs_dev; + file_stat.ino = fs_ino; + file_stat.nlink = fs_nlink; + file_stat.atim = fs_atim; + file_stat.mtim = fs_mtim; + file_stat.ctim = fs_ctim; + + return [file_stat, error]; + } + + path_filestat_set_times( + fd: number, + flags: number, + path: Uint8Array, + st_atim: bigint, + st_mtim: bigint, + fst_flags: number, + ): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u16 = new Uint16Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 30); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u32, 2, flags); + const [ptr, len] = this.allocator.block_write( + path, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 3, + ); + Atomics.store(func_sig_view_u64, 3, st_atim); + Atomics.store(func_sig_view_u64, 4, st_mtim); + Atomics.store(func_sig_view_u16, 12, fst_flags); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr, len); + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + path_link( + old_fd: number, + old_flags: number, + old_path: Uint8Array, + new_fd: number, + new_path: Uint8Array, + ): number { + this.lock_double_fd(old_fd, new_fd); + + const bytes_offset = old_fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 31); + Atomics.store(func_sig_view_u32, 1, old_fd); + Atomics.store(func_sig_view_u32, 2, old_flags); + const [ptr1, len1] = this.allocator.block_write( + old_path, + this.fd_func_sig, + old_fd * fd_func_sig_u32_size + 3, + ); + Atomics.store(func_sig_view_u32, 5, new_fd); + const [ptr2, len2] = this.allocator.block_write( + new_path, + this.fd_func_sig, + old_fd * fd_func_sig_u32_size + 6, + ); + + if (!this.call_fd_func(old_fd)) { + this.allocator.free(ptr1, len1); + this.allocator.free(ptr2, len2); + this.release_double_fd(old_fd, new_fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(old_fd); + + this.release_double_fd(old_fd, new_fd); + + return error; + } + + path_open( + fd: number, + dirflags: number, + path: Uint8Array, + oflags: number, + fs_rights_base: bigint, + fs_rights_inheriting: bigint, + fs_flags: number, + ): [number | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u16 = new Uint16Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + const func_sig_view_u64 = new BigUint64Array( + this.fd_func_sig, + bytes_offset, + ); + + Atomics.store(func_sig_view_u32, 0, 32); + Atomics.store(func_sig_view_u32, 1, fd); + Atomics.store(func_sig_view_u32, 2, dirflags); + const [ptr, len] = this.allocator.block_write( + path, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 3, + ); + Atomics.store(func_sig_view_u32, 5, oflags); + Atomics.store(func_sig_view_u64, 3, fs_rights_base); + Atomics.store(func_sig_view_u64, 4, fs_rights_inheriting); + Atomics.store(func_sig_view_u16, 20, fs_flags); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr, len); + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + if (error === wasi.ERRNO_SUCCESS) { + const new_fd = Atomics.load(func_sig_view_u32, 0); + this.release_fd(fd); + return [new_fd, error]; + } + + this.release_fd(fd); + + return [undefined, error]; + } + + path_readlink( + fd: number, + path: Uint8Array, + buf_len: number, + ): [Uint8Array | undefined, number] { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 33); + Atomics.store(func_sig_view_u32, 1, fd); + const [ptr, len] = this.allocator.block_write( + path, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 2, + ); + Atomics.store(func_sig_view_u32, 4, buf_len); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr, len); + this.release_fd(fd); + return [undefined, wasi.ERRNO_BADF]; + } + + const error = this.get_error(fd); + + const nread = Atomics.load(func_sig_view_u32, 0); + const ret_path_ptr = Atomics.load(func_sig_view_u32, 1); + const ret_path_len = Atomics.load(func_sig_view_u32, 2); + + this.release_fd(fd); + if (error !== wasi.ERRNO_SUCCESS && error !== wasi.ERRNO_NAMETOOLONG) { + this.allocator.free(ret_path_ptr, ret_path_len); + return [undefined, error]; + } + + const ret_path = new Uint8Array( + this.allocator.get_memory(ret_path_ptr, ret_path_len), + ); + const ret_path_slice = ret_path.slice(0, nread); + + return [ret_path_slice, error]; + } + + path_remove_directory(fd: number, path: Uint8Array): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 34); + Atomics.store(func_sig_view_u32, 1, fd); + const [ptr, len] = this.allocator.block_write( + path, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 2, + ); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr, len); + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + path_rename( + old_fd: number, + old_path: Uint8Array, + new_fd: number, + new_path: Uint8Array, + ): number { + this.lock_double_fd(old_fd, new_fd); + + const bytes_offset = old_fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 35); + Atomics.store(func_sig_view_u32, 1, old_fd); + const [ptr1, len1] = this.allocator.block_write( + old_path, + this.fd_func_sig, + old_fd * fd_func_sig_u32_size + 2, + ); + Atomics.store(func_sig_view_u32, 4, new_fd); + const [ptr2, len2] = this.allocator.block_write( + new_path, + this.fd_func_sig, + old_fd * fd_func_sig_u32_size + 5, + ); + + if (!this.call_fd_func(old_fd)) { + this.allocator.free(ptr1, len1); + this.allocator.free(ptr2, len2); + this.release_double_fd(old_fd, new_fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(old_fd); + + this.release_double_fd(old_fd, new_fd); + + return error; + } + + path_symlink(old_path: Uint8Array, fd: number, new_path: Uint8Array): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 36); + const [ptr1, len1] = this.allocator.block_write( + old_path, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 1, + ); + Atomics.store(func_sig_view_u32, 3, fd); + const [ptr2, len2] = this.allocator.block_write( + new_path, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 4, + ); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr1, len1); + this.allocator.free(ptr2, len2); + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } + + path_unlink_file(fd: number, path: Uint8Array): number { + this.lock_fd(fd); + + const bytes_offset = fd * fd_func_sig_bytes; + const func_sig_view_u32 = new Uint32Array(this.fd_func_sig, bytes_offset); + + Atomics.store(func_sig_view_u32, 0, 37); + Atomics.store(func_sig_view_u32, 1, fd); + const [ptr, len] = this.allocator.block_write( + path, + this.fd_func_sig, + fd * fd_func_sig_u32_size + 2, + ); + + if (!this.call_fd_func(fd)) { + this.allocator.free(ptr, len); + this.release_fd(fd); + return wasi.ERRNO_BADF; + } + + const error = this.get_error(fd); + + this.release_fd(fd); + + return error; + } +} diff --git a/threads/src/shared_array_buffer/sender.ts b/threads/src/shared_array_buffer/sender.ts new file mode 100644 index 0000000..510df3a --- /dev/null +++ b/threads/src/shared_array_buffer/sender.ts @@ -0,0 +1,223 @@ +export type ToRefSenderUseArrayBufferObject = { + data_size: number; + share_arrays_memory?: SharedArrayBuffer; +}; + +// To ref sender abstract class +export abstract class ToRefSenderUseArrayBuffer { + // The structure is similar to an allocator, but the mechanism is different + + // Example of fd management + // This needs to be handled + // 1. Start of path_open + // 2. Removed by fd_close + // 2.1 Sent by ToRefSender + // 3. Reassigned by path_open + // < Closed by ToRefSender + // 3.1 The person who opened it can use it + // < Closed by ToRefSender — this alone will cause a bug + // Structurally, this shouldn't happen in the farm + + // In the end, when receiving from this function, it should be done on the first call of each function + + // The first 4 bytes are for lock value: i32 + // The next 4 bytes are the current number of data: m: i32 + // The next 4 bytes are the length of the area used by share_arrays_memory: n: i32 + // Data header + // 4 bytes: remaining target count + // 4 bytes: target count (n) + // n * 4 bytes: target allocation numbers + // Data + // data_size bytes: data + private share_arrays_memory: SharedArrayBuffer; + + // The size of the data + private data_size: number; + + constructor( + // data is Uint32Array + // and data_size is data.length + data_size: number, + max_share_arrays_memory: number = 100 * 1024, + share_arrays_memory?: SharedArrayBuffer, + ) { + this.data_size = data_size; + if (share_arrays_memory) { + this.share_arrays_memory = share_arrays_memory; + } else { + this.share_arrays_memory = new SharedArrayBuffer(max_share_arrays_memory); + } + const view = new Int32Array(this.share_arrays_memory); + Atomics.store(view, 0, 0); + Atomics.store(view, 1, 0); + Atomics.store(view, 2, 12); + } + + protected static init_self_inner(sl: ToRefSenderUseArrayBufferObject): { + data_size: number; + max_share_arrays_memory: number; + share_arrays_memory: SharedArrayBuffer; + } { + return { + data_size: sl.data_size, + // biome-ignore lint/style/noNonNullAssertion: + max_share_arrays_memory: sl.share_arrays_memory!.byteLength, + // biome-ignore lint/style/noNonNullAssertion: + share_arrays_memory: sl.share_arrays_memory!, + }; + } + + private async async_lock(): Promise { + const view = new Int32Array(this.share_arrays_memory); + // eslint-disable-next-line no-constant-condition + while (true) { + let lock: "not-equal" | "timed-out" | "ok"; + const { value } = Atomics.waitAsync(view, 0, 1); + if (value instanceof Promise) { + lock = await value; + } else { + lock = value; + } + if (lock === "timed-out") { + throw new Error("timed-out lock"); + } + const old = Atomics.compareExchange(view, 0, 0, 1); + if (old !== 0) { + continue; + } + break; + } + } + + private block_lock(): void { + // eslint-disable-next-line no-constant-condition + while (true) { + const view = new Int32Array(this.share_arrays_memory); + const lock = Atomics.wait(view, 0, 1); + if (lock === "timed-out") { + throw new Error("timed-out lock"); + } + const old = Atomics.compareExchange(view, 0, 0, 1); + if (old !== 0) { + continue; + } + break; + } + } + + private release_lock(): void { + const view = new Int32Array(this.share_arrays_memory); + Atomics.store(view, 0, 0); + Atomics.notify(view, 0, 1); + } + + protected async async_send( + targets: Array, + data: Uint32Array, + ): Promise { + await this.async_lock(); + + const view = new Int32Array(this.share_arrays_memory); + const used_len = Atomics.load(view, 2); + const data_len = data.byteLength; + if (data_len !== this.data_size) { + throw new Error(`invalid data size: ${data_len} !== ${this.data_size}`); + } + const new_used_len = used_len + data_len + 8 + targets.length * 4; + if (new_used_len > this.share_arrays_memory.byteLength) { + throw new Error("over memory"); + } + + Atomics.store(view, 2, new_used_len); + + const header = new Int32Array(this.share_arrays_memory, used_len); + header[0] = targets.length; + header[1] = targets.length; + header.set(targets, 2); + + const data_view = new Uint32Array( + this.share_arrays_memory, + used_len + 8 + targets.length * 4, + ); + data_view.set(data); + + // console.log("async_send send", targets, data); + + Atomics.add(view, 1, 1); + + this.release_lock(); + } + + protected get_data(id: number): Array | undefined { + const view = new Int32Array(this.share_arrays_memory); + const data_num_tmp = Atomics.load(view, 1); + if (data_num_tmp === 0) { + return undefined; + } + + this.block_lock(); + + const data_num = Atomics.load(view, 1); + + const return_data: Array = []; + + let offset = 12; + // console.log("data_num", data_num); + for (let i = 0; i < data_num; i++) { + // console.log("this.share_arrays_memory", this.share_arrays_memory); + const header = new Int32Array(this.share_arrays_memory, offset); + const target_num = header[1]; + const targets = new Int32Array( + this.share_arrays_memory, + offset + 8, + target_num, + ); + const data_len = this.data_size; + if (targets.includes(id)) { + const data = new Uint32Array( + this.share_arrays_memory, + offset + 8 + target_num * 4, + data_len / 4, + ); + + // なぜかわからないが、上では正常に動作せず、以下のようにすると動作する + // return_data.push(new Uint32Array(data)); + return_data.push(new Uint32Array([...data])); + + const target_index = targets.indexOf(id); + Atomics.store(targets, target_index, -1); + const old_left_targets_num = Atomics.sub(header, 0, 1); + if (old_left_targets_num === 1) { + // rm data + Atomics.sub(view, 1, 1); + const used_len = Atomics.load(view, 2); + const new_used_len = used_len - data_len - 8 - target_num * 4; + Atomics.store(view, 2, new_used_len); + const next_data_offset = offset + data_len + 8 + target_num * 4; + const next_tail = new Int32Array( + this.share_arrays_memory, + next_data_offset, + ); + const now_tail = new Int32Array(this.share_arrays_memory, offset); + now_tail.set(next_tail); + // console.log("new_used_len", new_used_len); + } else { + offset += data_len + 8 + target_num * 4; + } + } else { + offset += data_len + 8 + target_num * 4; + } + // console.log("offset", offset); + } + + if (offset !== Atomics.load(view, 2)) { + throw new Error(`invalid offset: ${offset} !== ${Atomics.load(view, 2)}`); + } + + this.release_lock(); + + // console.log("get_data get: return_data", return_data); + + return return_data; + } +} diff --git a/threads/src/shared_array_buffer/serialize_error.ts b/threads/src/shared_array_buffer/serialize_error.ts new file mode 100644 index 0000000..fe78306 --- /dev/null +++ b/threads/src/shared_array_buffer/serialize_error.ts @@ -0,0 +1,23 @@ +export type SerializedError = { + message: string; + name: string; + stack?: string; + cause?: unknown; +}; + +export const serialize = (error: Error): SerializedError => { + return { + message: error.message, + name: error.name, + stack: error.stack, + cause: error.cause, + }; +}; + +export const deserialize = (serializedError: SerializedError): Error => { + const error = new Error(serializedError.message); + error.name = serializedError.name; + error.stack = serializedError.stack?.replace(/.wasm:/g, ".wasm\n"); + error.cause = serializedError.cause; + return error; +}; diff --git a/threads/src/shared_array_buffer/thread_spawn.ts b/threads/src/shared_array_buffer/thread_spawn.ts new file mode 100644 index 0000000..835e90c --- /dev/null +++ b/threads/src/shared_array_buffer/thread_spawn.ts @@ -0,0 +1,418 @@ +// (export "wasi_thread_start" (func $61879)) +// (func $61879 (param $0 i32) (param $1 i32) +// (local $2 i32) +// (local $3 i32) +// (local $4 i32) +// (local $5 i32) +// (local $6 i32) +// (local $7 i32) +// (global.set $global$0 +// (i32.load +// (local.get $1) +// ) +// ) + +// (import "wasi" "thread-spawn" (func $fimport$27 (param i32) (result i32))) + +import { WASIFarmAnimal } from "../animals.js"; +import type { WASIFarmRefObject } from "../ref.js"; +import type { WorkerBackgroundRefObject } from "./worker_background/index.js"; +import { + WorkerBackgroundRef, + worker_background_worker_url, +} from "./worker_background/index.js"; +import { WorkerBackgroundRefObjectConstructor } from "./worker_background/worker_export.js"; + +type ThreadSpawnerObject = { + share_memory: WebAssembly.Memory; + wasi_farm_refs_object: Array; + worker_url: string; + worker_background_ref_object: WorkerBackgroundRefObject; + // inst_default_buffer_kept: WebAssembly.Memory; +}; + +export class ThreadSpawner { + private share_memory: WebAssembly.Memory; + private wasi_farm_refs_object: Array; + private worker_url: string; + private worker_background_ref: WorkerBackgroundRef; + private worker_background_ref_object: WorkerBackgroundRefObject; + // inst_default_buffer_kept: WebAssembly.Memory; + + // hold the worker to prevent GC. + private worker_background_worker?: Worker; + private worker_background_worker_promise?: Promise; + + // https://github.com/rustwasm/wasm-pack/issues/479 + + constructor( + worker_url: string, + wasi_farm_refs_object: Array, + share_memory?: WebAssembly.Memory, + // 16MB for the time being. + // https://users.rust-lang.org/t/what-is-the-size-limit-of-threads-stack-in-rust/11867/3 + MIN_STACK = 16777216, + worker_background_ref_object?: WorkerBackgroundRefObject, + thread_spawn_wasm?: WebAssembly.Module, + // inst_default_buffer_kept?: WebAssembly.Memory, + ) { + this.worker_url = worker_url; + this.wasi_farm_refs_object = wasi_farm_refs_object; + + const min_initial_size = 1048576 / 65536; // Rust's default stack size is 1MB. + const initial_size = MIN_STACK / 65536; + if (initial_size < min_initial_size) { + throw new Error( + `The stack size must be at least ${min_initial_size} bytes.`, + ); + } + const max_memory = 1073741824 / 65536; // Rust's default maximum memory size is 1GB. + + // this.inst_default_buffer_kept = + // inst_default_buffer_kept || + // new WebAssembly.Memory({ + // initial: 1, + // maximum: max_memory, + // shared: true, + // }); + + this.share_memory = + share_memory || + // WebAssembly.Memory's 1 page is 65536 bytes. + new WebAssembly.Memory({ + initial: initial_size, + maximum: max_memory, + shared: true, + }); + + if (worker_background_ref_object === undefined) { + const worker_background_worker_url__ = worker_background_worker_url(); + this.worker_background_worker = new Worker( + worker_background_worker_url__, + { type: "module" }, + ); + URL.revokeObjectURL(worker_background_worker_url__); + const { promise, resolve } = Promise.withResolvers(); + this.worker_background_worker_promise = promise; + this.worker_background_worker.onmessage = () => { + this.worker_background_worker_promise = undefined; + resolve(); + }; + this.worker_background_ref_object = + WorkerBackgroundRefObjectConstructor(); + this.worker_background_ref = WorkerBackgroundRef.init_self( + this.worker_background_ref_object, + ); + this.worker_background_worker.postMessage({ + override_object: { + sl_object: this.get_object(), + thread_spawn_wasm, + }, + worker_background_ref_object: this.worker_background_ref_object, + }); + } else { + this.worker_background_ref_object = worker_background_ref_object; + this.worker_background_ref = WorkerBackgroundRef.init_self( + this.worker_background_ref_object, + ); + } + } + + // This cannot blocking. + async wait_worker_background_worker(): Promise { + if (this.worker_background_worker_promise) { + const promise = this.worker_background_worker_promise; + + await promise; + + return; + } + return; + } + + check_worker_background_worker(): void { + if (this.worker_background_worker_promise) { + throw new Error("worker_background_worker is not ready."); + } + } + + thread_spawn( + start_arg: number, + args: Array, + env: Array, + fd_map: Array<[number, number]>, + ): number { + const worker = this.worker_background_ref.new_worker( + this.worker_url, + { type: "module" }, + { + this_is_thread_spawn: true, + start_arg, + args, + env, + fd_map, + }, + ); + + const thread_id = worker.get_id(); + + return thread_id; + } + + async async_start_on_thread( + args: Array, + env: Array, + fd_map: Array<[number, number]>, + ): Promise { + if (!self.Worker.toString().includes("[native code]")) { + if (self.Worker.toString().includes("function")) { + console.warn("SubWorker(new Worker on Worker) is polyfilled maybe."); + } else { + throw new Error("SubWorker(new Worker on Worker) is not supported."); + } + } + + await this.worker_background_ref.async_start_on_thread( + this.worker_url, + { type: "module" }, + { + this_is_thread_spawn: true, + this_is_start: true, + args, + env, + fd_map, + }, + ); + } + + block_start_on_thread( + args: Array, + env: Array, + fd_map: Array<[number, number]>, + ): void { + if (!self.Worker.toString().includes("[native code]")) { + if (self.Worker.toString().includes("function")) { + console.warn("SubWorker(new Worker on Worker) is polyfilled maybe."); + } else { + throw new Error("SubWorker(new Worker on Worker) is not supported."); + } + } + + this.worker_background_ref.block_start_on_thread( + this.worker_url, + { type: "module" }, + { + this_is_thread_spawn: true, + this_is_start: true, + args, + env, + fd_map, + }, + ); + } + + static init_self(sl: ThreadSpawnerObject): ThreadSpawner { + const thread_spawner = new ThreadSpawner( + sl.worker_url, + sl.wasi_farm_refs_object, + sl.share_memory, + undefined, + sl.worker_background_ref_object, + // undefined, + // sl.inst_default_buffer_kept, + ); + return thread_spawner; + } + + static init_self_with_worker_background_ref( + sl: ThreadSpawnerObject, + worker_background_ref_object: WorkerBackgroundRefObject, + ): ThreadSpawner { + const thread_spawner = new ThreadSpawner( + sl.worker_url, + sl.wasi_farm_refs_object, + sl.share_memory, + undefined, + worker_background_ref_object, + // undefined, + // sl.inst_default_buffer_kept, + ); + return thread_spawner; + } + + get_share_memory(): WebAssembly.Memory { + return this.share_memory; + } + + get_object(): ThreadSpawnerObject { + return { + share_memory: this.share_memory, + wasi_farm_refs_object: this.wasi_farm_refs_object, + worker_url: this.worker_url, + worker_background_ref_object: this.worker_background_ref_object, + // inst_default_buffer_kept: this.inst_default_buffer_kept, + }; + } + + done_notify(code: number): void { + this.worker_background_ref.done_notify(code); + } + + async async_wait_done_or_error(): Promise { + if (this.worker_background_worker === undefined) { + throw new Error("worker_background_worker is undefined."); + } + + return await this.worker_background_ref.async_wait_done_or_error(); + } + + block_wait_done_or_error(): number { + if (this.worker_background_worker === undefined) { + throw new Error("worker_background_worker is undefined."); + } + + return this.worker_background_ref.block_wait_done_or_error(); + } +} + +// send fd_map is not implemented yet. +// issue: the fd passed to the child process is different from the parent process. +export const thread_spawn_on_worker = async (msg: { + this_is_thread_spawn: boolean; + worker_id?: number; + start_arg: number; + worker_background_ref: WorkerBackgroundRefObject; + sl_object: ThreadSpawnerObject; + thread_spawn_wasm: WebAssembly.Module; + args: Array; + env: Array; + fd_map: [number, number][]; + this_is_start?: boolean; +}): Promise => { + if (msg.this_is_thread_spawn) { + const { + sl_object, + fd_map, + worker_background_ref, + thread_spawn_wasm, + args, + env, + } = msg; + + const override_fd_map: Array = new Array( + sl_object.wasi_farm_refs_object.length, + ); + + // Possibly null (undefined) + for (const fd_and_wasi_ref_n of fd_map) { + // biome-ignore lint/suspicious/noDoubleEquals: + if (fd_and_wasi_ref_n == undefined) { + continue; + } + const [fd, wasi_ref_n] = fd_and_wasi_ref_n; + if (override_fd_map[wasi_ref_n] === undefined) { + override_fd_map[wasi_ref_n] = []; + } + override_fd_map[wasi_ref_n].push(fd); + } + + const thread_spawner = ThreadSpawner.init_self_with_worker_background_ref( + sl_object, + worker_background_ref, + ); + + if (msg.this_is_start) { + const wasi = new WASIFarmAnimal( + sl_object.wasi_farm_refs_object, + args, + env, + { + can_thread_spawn: true, + thread_spawn_worker_url: sl_object.worker_url, + hand_override_fd_map: fd_map, + }, + override_fd_map, + thread_spawner, + ); + + const inst = await WebAssembly.instantiate(thread_spawn_wasm, { + env: { + memory: wasi.get_share_memory(), + }, + wasi: wasi.wasiThreadImport, + wasi_snapshot_preview1: wasi.wasiImport, + }); + + wasi.start( + inst as unknown as { + exports: { + memory: WebAssembly.Memory; + _start: () => unknown; + }; + }, + ); + + globalThis.postMessage({ + msg: "done", + }); + + return wasi; + } + + const { worker_id: thread_id, start_arg } = msg; + + console.log(`thread_spawn worker ${thread_id} start`); + + const wasi = new WASIFarmAnimal( + sl_object.wasi_farm_refs_object, + args, + env, + { + can_thread_spawn: true, + thread_spawn_worker_url: sl_object.worker_url, + hand_override_fd_map: fd_map, + }, + override_fd_map, + thread_spawner, + ); + + const inst = await WebAssembly.instantiate(thread_spawn_wasm, { + env: { + memory: wasi.get_share_memory(), + }, + wasi: wasi.wasiThreadImport, + wasi_snapshot_preview1: wasi.wasiImport, + }); + + globalThis.postMessage({ + msg: "ready", + }); + + try { + wasi.wasi_thread_start( + inst as unknown as { + exports: { + memory: WebAssembly.Memory; + wasi_thread_start: (thread_id: number, start_arg: number) => void; + }; + }, + // biome-ignore lint/style/noNonNullAssertion: + thread_id!, + start_arg, + ); + } catch (e) { + globalThis.postMessage({ + msg: "error", + error: e, + }); + + return wasi; + } + + globalThis.postMessage({ + msg: "done", + }); + + return wasi; + } +}; diff --git a/threads/src/shared_array_buffer/util.ts b/threads/src/shared_array_buffer/util.ts new file mode 100644 index 0000000..4ed230f --- /dev/null +++ b/threads/src/shared_array_buffer/util.ts @@ -0,0 +1,68 @@ +export const get_func_name_from_number = (num: number): string => { + switch (num) { + case 7: + return "fd_advise"; + case 8: + return "fd_allocate"; + case 9: + return "fd_close"; + case 10: + return "fd_datasync"; + case 11: + return "fd_fdstat_get"; + case 12: + return "fd_fdstat_set_flags"; + case 13: + return "fd_fdstat_set_rights"; + case 14: + return "fd_filestat_get"; + case 15: + return "fd_filestat_set_size"; + case 16: + return "fd_filestat_set_times"; + case 17: + return "fd_pread"; + case 18: + return "fd_prestat_get"; + case 19: + return "fd_prestat_dir_name"; + case 20: + return "fd_pwrite"; + case 21: + return "fd_read"; + case 22: + return "fd_readdir"; + case 23: + return "fd_renumber"; + case 24: + return "fd_seek"; + case 25: + return "fd_sync"; + case 26: + return "fd_tell"; + case 27: + return "fd_write"; + case 28: + return "path_create_directory"; + case 29: + return "path_filestat_get"; + case 30: + return "path_filestat_set_times"; + case 31: + return "path_link"; + case 32: + return "path_open"; + case 33: + return "path_readlink"; + case 34: + return "path_remove_directory"; + case 35: + return "path_rename"; + case 36: + return "path_symlink"; + case 37: + return "path_unlink_file"; + default: + return "unknown"; + } +}; diff --git a/threads/src/shared_array_buffer/worker_background/index.ts b/threads/src/shared_array_buffer/worker_background/index.ts new file mode 100644 index 0000000..eb3e67c --- /dev/null +++ b/threads/src/shared_array_buffer/worker_background/index.ts @@ -0,0 +1,10 @@ +import type { WorkerBackgroundRefObject } from "./worker_export.js"; +import { WorkerBackgroundRef, WorkerRef } from "./worker_background_ref.js"; +import { url as worker_background_worker_url } from "./worker_blob.js"; + +export { + WorkerBackgroundRef, + WorkerRef, + type WorkerBackgroundRefObject, + worker_background_worker_url, +}; diff --git a/threads/src/shared_array_buffer/worker_background/minify.js b/threads/src/shared_array_buffer/worker_background/minify.js new file mode 100644 index 0000000..9f4435e --- /dev/null +++ b/threads/src/shared_array_buffer/worker_background/minify.js @@ -0,0 +1,46 @@ +import swc from "@swc/core"; + +import { readFileSync, writeFileSync } from "node:fs"; + +const old_code = readFileSync( + "./dist/workers/worker_background_worker.js", + "utf8", +); + +const { code } = await swc.minify(old_code, { + compress: { + reduce_funcs: true, + arguments: true, + booleans_as_integers: true, + hoist_funs: false, + keep_classnames: false, + unsafe: true, + }, + mangle: true, +}); + +writeFileSync( + "./dist/workers/worker_background_worker_minify.js", + code, + "utf8", +); + +// \n -> \\n + +const wrapper_code = `export const url = () => { + const code = + '${code.replace(/\\/g, "\\\\")}'; + + const blob = new Blob([code], { type: "application/javascript" }); + + const url = URL.createObjectURL(blob); + + return url; +}; +`; + +writeFileSync( + "./src/shared_array_buffer/worker_background/worker_blob.ts", + wrapper_code, + "utf8", +); diff --git a/threads/src/shared_array_buffer/worker_background/spack.config.cjs b/threads/src/shared_array_buffer/worker_background/spack.config.cjs new file mode 100644 index 0000000..9e285a2 --- /dev/null +++ b/threads/src/shared_array_buffer/worker_background/spack.config.cjs @@ -0,0 +1,15 @@ +// https://swc.rs/docs/configuration/bundling + +const { config } = require("@swc/core/spack"); + +console.log(__dirname); + +module.exports = config({ + entry: { + web: `${__dirname}/worker.ts`, + }, + output: { + path: `${__dirname}/../../../dist/workers/`, + name: "worker_background_worker.js", + }, +}); diff --git a/threads/src/shared_array_buffer/worker_background/worker.ts b/threads/src/shared_array_buffer/worker_background/worker.ts new file mode 100644 index 0000000..a1bdfe4 --- /dev/null +++ b/threads/src/shared_array_buffer/worker_background/worker.ts @@ -0,0 +1,299 @@ +/// + +// If you create a worker and try to increase the number of threads, +// you will have to use Atomics.wait because they need to be synchronized. +// However, this is essentially impossible because Atomics.wait blocks the threads. +// Therefore, a dedicated worker that creates a subworker (worker in worker) is prepared. +// The request is made using BroadcastChannel. + +import * as Serializer from "../serialize_error.js"; +import { AllocatorUseArrayBuffer } from "../allocator.js"; +import type { WorkerBackgroundRefObject } from "./worker_export.js"; + +// Note that postMessage, etc. +// cannot be used in a blocking environment such as during wasm execution. +// (at least as far as I have tried) + +class WorkerBackground { + private override_object: T; + private allocator: AllocatorUseArrayBuffer; + private lock: SharedArrayBuffer; + private signature_input: SharedArrayBuffer; + + // worker_id starts from 1 + private workers: Array = [undefined]; + + private start_worker?: Worker; + + // @ts-ignore + private listen_holder: Promise; + + constructor( + override_object: T, + lock?: SharedArrayBuffer, + allocator?: AllocatorUseArrayBuffer, + signature_input?: SharedArrayBuffer, + ) { + this.override_object = override_object; + this.lock = lock ?? new SharedArrayBuffer(20); + this.allocator = + allocator ?? + new AllocatorUseArrayBuffer(new SharedArrayBuffer(10 * 1024)); + this.signature_input = signature_input ?? new SharedArrayBuffer(24); + this.listen_holder = this.listen(); + } + + static init_self( + override_object: T, + worker_background_ref_object: WorkerBackgroundRefObject, + ): WorkerBackground { + return new WorkerBackground( + override_object, + worker_background_ref_object.lock, + AllocatorUseArrayBuffer.init_self(worker_background_ref_object.allocator), + worker_background_ref_object.signature_input, + ); + } + + assign_worker_id(): number { + for (let i = 1; i < this.workers.length; i++) { + if (this.workers[i] === undefined) { + return i; + } + } + this.workers.push(undefined); + return this.workers.length - 1; + } + + ref(): WorkerBackgroundRefObject { + return { + allocator: this.allocator.get_object(), + lock: this.lock, + signature_input: this.signature_input, + }; + } + + async listen(): Promise { + const lock_view = new Int32Array(this.lock); + Atomics.store(lock_view, 0, 0); + Atomics.store(lock_view, 1, 0); + + const signature_input_view = new Int32Array(this.signature_input); + + // eslint-disable-next-line no-constant-condition + while (true) { + try { + let lock: "not-equal" | "timed-out" | "ok"; + + const { value } = Atomics.waitAsync(lock_view, 1, 0); + if (value instanceof Promise) { + lock = await value; + } else { + lock = value; + } + if (lock === "timed-out") { + throw new Error("timed-out"); + } + + const locked_value = Atomics.load(lock_view, 1); + if (locked_value !== 1) { + throw new Error("locked"); + } + + const gen_worker = () => { + console.log("gen_worker"); + const url_ptr = Atomics.load(signature_input_view, 1); + const url_len = Atomics.load(signature_input_view, 2); + const url_buff = this.allocator.get_memory(url_ptr, url_len); + this.allocator.free(url_ptr, url_len); + const url = new TextDecoder().decode(url_buff); + const is_module = Atomics.load(signature_input_view, 3) === 1; + return new Worker(url, { + type: is_module ? "module" : "classic", + }); + }; + + // biome-ignore lint/complexity/noBannedTypes: + const gen_obj = (): Object => { + console.log("gen_obj"); + const json_ptr = Atomics.load(signature_input_view, 4); + const json_len = Atomics.load(signature_input_view, 5); + const json_buff = this.allocator.get_memory(json_ptr, json_len); + this.allocator.free(json_ptr, json_len); + const json = new TextDecoder().decode(json_buff); + // biome-ignore lint/complexity/noBannedTypes: + return JSON.parse(json) as Object; + }; + + const signature_input = Atomics.load(signature_input_view, 0); + switch (signature_input) { + // create new worker + case 1: { + const worker = gen_worker(); + const obj = gen_obj(); + + const worker_id = this.assign_worker_id(); + + console.log(`new worker ${worker_id}`); + + this.workers[worker_id] = worker; + + const { promise, resolve } = Promise.withResolvers(); + + worker.onmessage = async (e) => { + const { msg } = e.data; + + if (msg === "ready") { + resolve(); + } + + if (msg === "done") { + // biome-ignore lint/style/noNonNullAssertion: + this.workers[worker_id]!.terminate(); + this.workers[worker_id] = undefined; + + console.log(`worker ${worker_id} done so terminate`); + } + + if (msg === "error") { + // biome-ignore lint/style/noNonNullAssertion: + this.workers[worker_id]!.terminate(); + this.workers[worker_id] = undefined; + + let n = 0; + for (const worker of this.workers) { + if (worker !== undefined) { + worker.terminate(); + console.warn( + `wasi throw error but child process exists, terminate ${n}`, + ); + } + n++; + } + if (this.start_worker !== undefined) { + this.start_worker.terminate(); + console.warn( + "wasi throw error but wasi exists, terminate wasi", + ); + } + + this.workers = [undefined]; + this.start_worker = undefined; + + const error = e.data.error; + + const notify_view = new Int32Array(this.lock, 8); + + const serialized_error = Serializer.serialize(error); + + const [ptr, len] = await this.allocator.async_write( + new TextEncoder().encode(JSON.stringify(serialized_error)), + this.lock, + 3, + ); + + // notify error = code 1 + const old = Atomics.compareExchange(notify_view, 0, 0, 1); + + if (old !== 0) { + console.error("what happened?"); + + this.allocator.free(ptr, len); + + return; + } + + const num = Atomics.notify(notify_view, 0); + + if (num === 0) { + console.error(error); + + this.allocator.free(ptr, len); + + Atomics.store(notify_view, 0, 0); + } + } + }; + + worker.postMessage({ + ...this.override_object, + ...obj, + worker_id, + worker_background_ref: this.ref(), + }); + + await promise; + + Atomics.store(signature_input_view, 0, worker_id); + + break; + } + // create start + case 2: { + this.start_worker = gen_worker(); + const obj = gen_obj(); + + this.start_worker.onmessage = async (e) => { + const { msg } = e.data; + + if (msg === "done") { + let n = 0; + for (const worker of this.workers) { + if (worker !== undefined) { + worker.terminate(); + console.warn(`wasi done but worker exists, terminate ${n}`); + } + n++; + } + + // biome-ignore lint/style/noNonNullAssertion: + this.start_worker!.terminate(); + this.start_worker = undefined; + + console.log("start worker done so terminate"); + } + }; + + this.start_worker.postMessage({ + ...this.override_object, + ...obj, + worker_background_ref: this.ref(), + }); + + break; + } + } + + const old_call_lock = Atomics.exchange(lock_view, 1, 0); + if (old_call_lock !== 1) { + throw new Error("Lock is already set"); + } + const num = Atomics.notify(lock_view, 1, 1); + if (num !== 1) { + if (num === 0) { + console.warn("notify failed, waiter is late"); + continue; + } + throw new Error(`notify failed: ${num}`); + } + } catch (e) { + console.error(e); + + // sleep 1000 + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + } + } +} + +// @ts-ignore +let worker_background: WorkerBackground; + +globalThis.onmessage = (e: MessageEvent) => { + const { override_object, worker_background_ref_object } = e.data; + worker_background = WorkerBackground.init_self( + override_object, + worker_background_ref_object, + ); + postMessage("ready"); +}; diff --git a/threads/src/shared_array_buffer/worker_background/worker_background_ref.ts b/threads/src/shared_array_buffer/worker_background/worker_background_ref.ts new file mode 100644 index 0000000..17e9924 --- /dev/null +++ b/threads/src/shared_array_buffer/worker_background/worker_background_ref.ts @@ -0,0 +1,314 @@ +import { AllocatorUseArrayBuffer } from "../allocator.js"; +import type { + WorkerBackgroundRefObject, + WorkerOptions, +} from "./worker_export.js"; +import * as Serializer from "../serialize_error.js"; + +export class WorkerBackgroundRef { + private allocator: AllocatorUseArrayBuffer; + private lock: SharedArrayBuffer; + private signature_input: SharedArrayBuffer; + + constructor( + allocator: AllocatorUseArrayBuffer, + lock: SharedArrayBuffer, + signature_input: SharedArrayBuffer, + ) { + this.allocator = allocator; + this.lock = lock; + this.signature_input = signature_input; + } + + private block_lock_base_func(): void { + const view = new Int32Array(this.lock); + // eslint-disable-next-line no-constant-condition + while (true) { + const lock = Atomics.wait(view, 0, 1); + if (lock === "timed-out") { + throw new Error("timed-out lock"); + } + const old = Atomics.compareExchange(view, 0, 0, 1); + if (old !== 0) { + continue; + } + break; + } + } + + private async async_lock_base_func(): Promise { + const view = new Int32Array(this.lock); + // eslint-disable-next-line no-constant-condition + while (true) { + let value: "timed-out" | "not-equal" | "ok"; + const { value: _value } = Atomics.waitAsync(view, 0, 1); + if (_value instanceof Promise) { + value = await _value; + } else { + value = _value; + } + if (value === "timed-out") { + throw new Error("timed-out lock"); + } + const old = Atomics.compareExchange(view, 0, 0, 1); + if (old !== 0) { + continue; + } + break; + } + } + + private call_base_func(): void { + const view = new Int32Array(this.lock); + const old = Atomics.exchange(view, 1, 1); + if (old !== 0) { + console.error("what happened?"); + } + Atomics.notify(view, 1, 1); + } + + // wait base_func + private block_wait_base_func(): void { + const view = new Int32Array(this.lock); + const lock = Atomics.wait(view, 1, 1); + if (lock === "timed-out") { + throw new Error("timed-out lock"); + } + } + + private async async_wait_base_func(): Promise { + const view = new Int32Array(this.lock); + let value: "timed-out" | "not-equal" | "ok"; + const { value: _value } = Atomics.waitAsync(view, 1, 1); + if (_value instanceof Promise) { + value = await _value; + } else { + value = _value; + } + if (value === "timed-out") { + throw new Error("timed-out lock"); + } + } + + // release base_func + private release_base_func(): void { + const view = new Int32Array(this.lock); + Atomics.store(view, 0, 0); + Atomics.notify(view, 0, 1); + } + + new_worker( + url: string, + options?: WorkerOptions, + post_obj?: unknown, + ): WorkerRef { + this.block_lock_base_func(); + const view = new Int32Array(this.signature_input); + Atomics.store(view, 0, 1); + const url_buffer = new TextEncoder().encode(url); + this.allocator.block_write(url_buffer, this.signature_input, 1); + Atomics.store(view, 3, options?.type === "module" ? 1 : 0); + const obj_json = JSON.stringify(post_obj); + const obj_buffer = new TextEncoder().encode(obj_json); + this.allocator.block_write(obj_buffer, this.signature_input, 4); + this.call_base_func(); + this.block_wait_base_func(); + + const id = Atomics.load(view, 0); + + this.release_base_func(); + + return new WorkerRef(id); + } + + async async_start_on_thread( + url: string, + options: WorkerOptions | undefined, + post_obj: unknown, + ) { + await this.async_lock_base_func(); + const view = new Int32Array(this.signature_input); + Atomics.store(view, 0, 2); + const url_buffer = new TextEncoder().encode(url); + await this.allocator.async_write(url_buffer, this.signature_input, 1); + Atomics.store(view, 3, options?.type === "module" ? 1 : 0); + const obj_json = JSON.stringify(post_obj); + const obj_buffer = new TextEncoder().encode(obj_json); + await this.allocator.async_write(obj_buffer, this.signature_input, 4); + this.call_base_func(); + await this.async_wait_base_func(); + + this.release_base_func(); + } + + block_start_on_thread( + url: string, + options: WorkerOptions | undefined, + post_obj: unknown, + ) { + this.block_lock_base_func(); + const view = new Int32Array(this.signature_input); + Atomics.store(view, 0, 2); + const url_buffer = new TextEncoder().encode(url); + this.allocator.block_write(url_buffer, this.signature_input, 1); + Atomics.store(view, 3, options?.type === "module" ? 1 : 0); + const obj_json = JSON.stringify(post_obj); + const obj_buffer = new TextEncoder().encode(obj_json); + this.allocator.block_write(obj_buffer, this.signature_input, 4); + this.call_base_func(); + this.block_wait_base_func(); + + this.release_base_func(); + } + + static init_self(sl: WorkerBackgroundRefObject): WorkerBackgroundRef { + return new WorkerBackgroundRef( + AllocatorUseArrayBuffer.init_self(sl.allocator), + sl.lock, + sl.signature_input, + ); + } + + done_notify(code: number): void { + const notify_view = new Int32Array(this.lock, 8); + + // notify done = code 2 + const old = Atomics.compareExchange(notify_view, 0, 0, 2); + + if (old !== 0) { + console.error("what happened?"); + + return; + } + + Atomics.store(notify_view, 1, code); + + const num = Atomics.notify(notify_view, 0); + + if (num === 0) { + Atomics.store(notify_view, 0, 0); + } + } + + async async_wait_done_or_error(): Promise { + const notify_view = new Int32Array(this.lock, 8); + + Atomics.store(notify_view, 0, 0); + + let value: "timed-out" | "not-equal" | "ok"; + const { value: _value } = Atomics.waitAsync(notify_view, 0, 0); + if (_value instanceof Promise) { + value = await _value; + } else { + value = _value; + } + + if (value === "timed-out") { + throw new Error("timed-out"); + } + + if (value === "not-equal") { + throw new Error("not-equal"); + } + + const code = Atomics.load(notify_view, 0); + + if (code === 2) { + const old = Atomics.compareExchange(notify_view, 0, 2, 0); + + const code = Atomics.load(notify_view, 1); + + if (old !== 2) { + console.error("what happened?"); + } + + return code; + } + + if (code !== 1) { + throw new Error("unknown code"); + } + + // get error + const ptr = Atomics.load(notify_view, 1); + const size = Atomics.load(notify_view, 2); + const error_buffer = this.allocator.get_memory(ptr, size); + const error_txt = new TextDecoder().decode(error_buffer); + const error_serialized = JSON.parse( + error_txt, + ) as Serializer.SerializedError; + const error = Serializer.deserialize(error_serialized); + + const old = Atomics.compareExchange(notify_view, 0, 1, 0); + + if (old !== 1) { + console.error("what happened?"); + } + + throw error; + } + + block_wait_done_or_error(): number { + const notify_view = new Int32Array(this.lock, 8); + + Atomics.store(notify_view, 0, 0); + + const value = Atomics.wait(notify_view, 0, 0); + + if (value === "timed-out") { + throw new Error("timed-out"); + } + + if (value === "not-equal") { + throw new Error("not-equal"); + } + + const code = Atomics.load(notify_view, 0); + + if (code === 2) { + const old = Atomics.compareExchange(notify_view, 0, 2, 0); + + const code = Atomics.load(notify_view, 1); + + if (old !== 2) { + console.error("what happened?"); + } + + return code; + } + + if (code !== 1) { + throw new Error("unknown code"); + } + + // get error + const ptr = Atomics.load(notify_view, 1); + const size = Atomics.load(notify_view, 2); + const error_buffer = this.allocator.get_memory(ptr, size); + const error_txt = new TextDecoder().decode(error_buffer); + const error_serialized = JSON.parse( + error_txt, + ) as Serializer.SerializedError; + const error = Serializer.deserialize(error_serialized); + + const old = Atomics.compareExchange(notify_view, 0, 1, 0); + + if (old !== 1) { + console.error("what happened?"); + } + + throw error; + } +} + +export class WorkerRef { + private id: number; + + constructor(id: number) { + this.id = id; + } + + get_id(): number { + return this.id; + } +} diff --git a/threads/src/shared_array_buffer/worker_background/worker_blob.ts b/threads/src/shared_array_buffer/worker_background/worker_blob.ts new file mode 100644 index 0000000..b47847b --- /dev/null +++ b/threads/src/shared_array_buffer/worker_background/worker_blob.ts @@ -0,0 +1,10 @@ +export const url = () => { + const code = + 'let worker_background;let serialize=r=>({message:r.message,name:r.name,stack:r.stack,cause:r.cause});class AllocatorUseArrayBuffer{share_arrays_memory;constructor(r=new SharedArrayBuffer(0xa00000)){this.share_arrays_memory=r;let e=new Int32Array(this.share_arrays_memory);Atomics.store(e,0,0),Atomics.store(e,1,0),Atomics.store(e,2,12)}static init_self(r){return new AllocatorUseArrayBuffer(r.share_arrays_memory)}async async_write(r,e,t){let o=new Int32Array(this.share_arrays_memory);for(;;){let{value:s}=Atomics.waitAsync(o,0,1);if("timed-out"===(s instanceof Promise?await s:s))throw Error("timed-out lock");if(0!==Atomics.compareExchange(o,0,0,1))continue;let i=this.write_inner(r,e,t);return Atomics.store(o,0,0),Atomics.notify(o,0,1),i}}block_write(r,e,t){for(;;){let o=new Int32Array(this.share_arrays_memory);if("timed-out"===Atomics.wait(o,0,1))throw Error("timed-out lock");if(0!==Atomics.compareExchange(o,0,0,1))continue;let s=this.write_inner(r,e,t);return Atomics.store(o,0,0),Atomics.notify(o,0,1),s}}write_inner(r,e,t){let o,s;let i=new Int32Array(this.share_arrays_memory),a=new Uint8Array(this.share_arrays_memory);o=0===Atomics.add(i,1,1)?Atomics.store(i,2,12):Atomics.load(i,2);let n=this.share_arrays_memory.byteLength,l=r.byteLength,c=o+l;if(n{console.log("gen_worker");let r=Atomics.load(e,1),t=Atomics.load(e,2),o=this.allocator.get_memory(r,t);this.allocator.free(r,t);let s=new TextDecoder().decode(o),i=1===Atomics.load(e,3);return new Worker(s,{type:i?"module":"classic"})},a=()=>{console.log("gen_obj");let r=Atomics.load(e,4),t=Atomics.load(e,5),o=this.allocator.get_memory(r,t);this.allocator.free(r,t);let s=new TextDecoder().decode(o);return JSON.parse(s)};switch(Atomics.load(e,0)){case 1:{let r=i(),t=a(),o=this.assign_worker_id();console.log("new worker "+o),this.workers[o]=r;let{promise:s,resolve:n}=Promise.withResolvers();r.onmessage=async r=>{let{msg:e}=r.data;if("ready"===e&&n(),"done"===e&&(this.workers[o].terminate(),this.workers[o]=void 0,console.log(`worker ${o} done so terminate`)),"error"===e){this.workers[o].terminate(),this.workers[o]=void 0;let e=0;for(let r of this.workers)void 0!==r&&(r.terminate(),console.warn("wasi throw error but child process exists, terminate "+e)),e++;void 0!==this.start_worker&&(this.start_worker.terminate(),console.warn("wasi throw error but wasi exists, terminate wasi")),this.workers=[void 0],this.start_worker=void 0;let t=r.data.error,s=new Int32Array(this.lock,8),i=serialize(t),[a,n]=await this.allocator.async_write(new TextEncoder().encode(JSON.stringify(i)),this.lock,3),l=Atomics.compareExchange(s,0,0,1);if(0!==l){console.error("what happened?"),this.allocator.free(a,n);return}let c=Atomics.notify(s,0);0===c&&(console.error(t),this.allocator.free(a,n),Atomics.store(s,0,0))}},r.postMessage({...this.override_object,...t,worker_id:o,worker_background_ref:this.ref()}),await s,Atomics.store(e,0,o);break}case 2:{this.start_worker=i();let r=a();this.start_worker.onmessage=async r=>{let{msg:e}=r.data;if("done"===e){let r=0;for(let e of this.workers)void 0!==e&&(e.terminate(),console.warn("wasi done but worker exists, terminate "+r)),r++;this.start_worker.terminate(),this.start_worker=void 0,console.log("start worker done so terminate")}},this.start_worker.postMessage({...this.override_object,...r,worker_background_ref:this.ref()})}}let n=Atomics.exchange(r,1,0);if(1!==n)throw Error("Lock is already set");let l=Atomics.notify(r,1,1);if(1!==l){if(0===l){console.warn("notify failed, waiter is late");continue}throw Error("notify failed: "+l)}}catch(r){console.error(r),await new Promise(r=>setTimeout(r,1e3))}}};globalThis.onmessage=r=>{let{override_object:e,worker_background_ref_object:t}=r.data;worker_background=WorkerBackground.init_self(e,t),postMessage("ready")};'; + + const blob = new Blob([code], { type: "application/javascript" }); + + const url = URL.createObjectURL(blob); + + return url; +}; diff --git a/threads/src/shared_array_buffer/worker_background/worker_export.ts b/threads/src/shared_array_buffer/worker_background/worker_export.ts new file mode 100644 index 0000000..f0758a2 --- /dev/null +++ b/threads/src/shared_array_buffer/worker_background/worker_export.ts @@ -0,0 +1,22 @@ +import type { AllocatorUseArrayBufferObject } from "../allocator.js"; + +export type WorkerBackgroundRefObject = { + allocator: AllocatorUseArrayBufferObject; + lock: SharedArrayBuffer; + signature_input: SharedArrayBuffer; +}; + +export const WorkerBackgroundRefObjectConstructor = + (): WorkerBackgroundRefObject => { + return { + allocator: { + share_arrays_memory: new SharedArrayBuffer(10 * 1024), + }, + lock: new SharedArrayBuffer(20), + signature_input: new SharedArrayBuffer(24), + }; + }; + +export type WorkerOptions = { + type: "module" | ""; +}; diff --git a/threads/tsconfig.json b/threads/tsconfig.json new file mode 100644 index 0000000..6a2b7c9 --- /dev/null +++ b/threads/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ESNext", "DOM"], + "skipLibCheck": true, + /* Bundler mode */ + "incremental": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true, + "outDir": "./dist", + "declarationDir": "./types", + "esModuleInterop": true, + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["./src"] +} diff --git a/threads/vite.config.ts b/threads/vite.config.ts new file mode 100644 index 0000000..a358b42 --- /dev/null +++ b/threads/vite.config.ts @@ -0,0 +1,28 @@ +// https://zenn.dev/seapolis/articles/3605c4befc8465 + +import { resolve } from "node:path"; +import { defineConfig } from "vite"; +import dts from "vite-plugin-dts"; +import swc from "unplugin-swc"; + +export default defineConfig({ + server: { + headers: { + "Cross-Origin-Embedder-Policy": "require-corp", + "Cross-Origin-Opener-Policy": "same-origin", + }, + }, + build: { + lib: { + entry: resolve(__dirname, "src/index.ts"), + name: "wasi-shim-threads", + formats: ["es", "umd", "cjs"], + fileName: (format) => `browser-wasi-shim-threads.${format}.js`, + }, + sourcemap: true, + minify: true, + copyPublicDir: false, + }, + // plugins: [dts({ rollupTypes: true })], + plugins: [swc.vite(), swc.rollup(), dts({ rollupTypes: true })], +});