From a0e515d66b1151dd70ef6795a15f4e8d8f1146c6 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Fri, 20 Oct 2023 13:59:48 -0700 Subject: [PATCH 01/25] wip - 2023-10-20 6:00pm: hey, the tests pass now - 2023-10-20 11:00pm: hey! we're passing data around - tomorrow: - implement host->guest and host->worker->guest calls - create a wasi - 2023-10-21 10:40am: stuff all of our internal config into an interface - I kind of think we want a `wasi: WASI | boolean` flag instead of `useWasi: boolean` - 2023-10-21 1:09am: update deps - 2023-10-22 10:51pm: whew! lots of stuff working now! next up: host functions! - 2023-10-23 2:51pm: okay, I have to do some more major surgery: callcontext needs to be instantiated per invocation and live canonically on the host thread - 2023-10-25 12:40pm: blocks are transferred between the main and background threads as-needed now. what a ride. - 2023-10-25 11:08pm: all of the tests from the original deno test suite pass now. - 2023-10-26 9:57am: we lint! - 2023-10-26 10:26am: all of the examples work! - 2023-10-26 12:51pm: add DEVELOPING.md, artifacts tests --- .eslintrc.yaml | 16 + .gitignore | 3 + DEVELOPING.md | 115 + Makefile | 32 - README.md | 57 +- build.js | 28 - deno.json | 8 + deno.lock | 24 + examples/deno.ts | 12 +- examples/index.html | 18 +- examples/node.js | 12 +- jest.config.js | 5 - justfile | 342 + package-lock.json | 9796 +++++-------------- package.json | 61 +- playwright.config.js | 14 + src/background-plugin.ts | 419 + src/browser/index.ts | 175 - src/call-context.ts | 348 + src/deno/mod.ts | 121 - src/foreground-plugin.ts | 176 + src/manifest.ts | 174 + src/mod.test.ts | 321 + src/mod.ts | 128 + src/node/index.ts | 158 - src/plugin-options.ts | 41 + src/plugin.ts | 704 -- src/polyfills/browser:features.ts | 19 + src/polyfills/browser:fs.ts | 3 + src/polyfills/browser:wasi.ts | 35 + src/polyfills/deno:features.ts | 15 + src/polyfills/deno:wasi.ts | 30 + src/polyfills/host:node:worker_threads.ts | 34 + src/polyfills/node:features.ts | 15 + src/polyfills/node:fs.ts | 1 + src/polyfills/node:wasi.ts | 30 + src/polyfills/worker:node:worker_threads.ts | 16 + src/runtime.ts | 3 + src/worker-url.ts | 2 + src/worker.ts | 304 + tests/index.test.ts | 212 - tests/mod.test.ts | 210 - tests/playwright.test.js | 23 + tsconfig.browser.json | 6 - tsconfig.json | 13 +- tsconfig.node.json | 6 - tslint.json | 6 - types/js-sdk:features/index.d.ts | 3 + types/js-sdk:fs/index.d.ts | 3 + types/js-sdk:wasi/index.d.ts | 7 + types/js-sdk:worker-url/index.d.ts | 3 + update-kernel.js | 27 - 52 files changed, 5245 insertions(+), 9089 deletions(-) create mode 100644 .eslintrc.yaml create mode 100644 DEVELOPING.md delete mode 100644 Makefile delete mode 100644 build.js create mode 100644 deno.json create mode 100644 deno.lock mode change 100644 => 100755 examples/deno.ts mode change 100644 => 100755 examples/node.js delete mode 100644 jest.config.js create mode 100644 justfile create mode 100644 playwright.config.js create mode 100644 src/background-plugin.ts delete mode 100644 src/browser/index.ts create mode 100644 src/call-context.ts delete mode 100644 src/deno/mod.ts create mode 100644 src/foreground-plugin.ts create mode 100644 src/manifest.ts create mode 100644 src/mod.test.ts create mode 100644 src/mod.ts delete mode 100644 src/node/index.ts create mode 100644 src/plugin-options.ts delete mode 100644 src/plugin.ts create mode 100644 src/polyfills/browser:features.ts create mode 100644 src/polyfills/browser:fs.ts create mode 100644 src/polyfills/browser:wasi.ts create mode 100644 src/polyfills/deno:features.ts create mode 100644 src/polyfills/deno:wasi.ts create mode 100644 src/polyfills/host:node:worker_threads.ts create mode 100644 src/polyfills/node:features.ts create mode 100644 src/polyfills/node:fs.ts create mode 100644 src/polyfills/node:wasi.ts create mode 100644 src/polyfills/worker:node:worker_threads.ts create mode 100644 src/runtime.ts create mode 100644 src/worker-url.ts create mode 100644 src/worker.ts delete mode 100644 tests/index.test.ts delete mode 100644 tests/mod.test.ts create mode 100644 tests/playwright.test.js delete mode 100644 tsconfig.browser.json delete mode 100644 tsconfig.node.json delete mode 100644 tslint.json create mode 100644 types/js-sdk:features/index.d.ts create mode 100644 types/js-sdk:fs/index.d.ts create mode 100644 types/js-sdk:wasi/index.d.ts create mode 100644 types/js-sdk:worker-url/index.d.ts delete mode 100644 update-kernel.js diff --git a/.eslintrc.yaml b/.eslintrc.yaml new file mode 100644 index 0000000..31be2f8 --- /dev/null +++ b/.eslintrc.yaml @@ -0,0 +1,16 @@ +env: + browser: true + es2021: true + node: true +extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'] +parser: '@typescript-eslint/parser' +root: true +ignorePatterns: ["oldsrc/", "tests/mod.test.ts", "tests/index.test.ts"] +rules: + no-constant-condition: 'off' + no-unused-vars: 'off' + '@typescript-eslint/no-unused-vars': + - 'error' + - argsIgnorePattern: '_.*' + varsIgnorePattern: '_.*' + '@typescript-eslint/no-explicit-any': 'off' diff --git a/.gitignore b/.gitignore index c6bba59..1507924 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# test artifacts +tests/artifacts + # Logs logs *.log diff --git a/DEVELOPING.md b/DEVELOPING.md new file mode 100644 index 0000000..f293449 --- /dev/null +++ b/DEVELOPING.md @@ -0,0 +1,115 @@ +# Developing + +## The build process + +The Extism SDK targets several platforms: + +- Deno +- Node ECMAScript Modules ("ESM") +- Node CommonJS Modules ("CJS") +- Browser ECMAScript Modules + +The source of this library is written as valid TypeScript, which may be +consumed and run directly by Deno. The latter three platforms are treated as +compile targets. There are two other compile targets: + +- The source of the [Worker](https://mdn.io/worker), compiled for the browser. +- The source of the [Worker](https://mdn.io/worker), compiled for node. +- Tests + +For compiled targets, the worker is compiled to a single artifact with an entry +point starting at `src/worker.ts`, base64-encoded, and included in the +resulting artifact. + +Builds are orchestrated by the `justfile` and `esbuild`: each build target recipe accepts +incoming `esbuild` flags as an array of JSON data and prepends its own configuration. +This allows dependent recipes to override earlier flags. An annotated example: + +``` +build_worker_browser out='worker/browser' args='[]': # <-- we accept args and an out dir + #!/bin/bash + config="$(<<<'{{ args }}' jq -cM ' + [{ + "format": "esm", + "alias": { + "js-sdk:features": "./src/polyfills/browser:features.ts", + "node:worker_threads": "./src/polyfills/worker:node:worker_threads.ts", + "js-sdk:fs": "./src/polyfills/browser:fs.ts", + "js-sdk:wasi": "./src/polyfills/browser:wasi.ts", + } + }] + . # <--- add this recipe's flags to the incoming flags. + ')" + just build_worker {{ out }} "$config" +``` + +There is a `_build` recipe that all other build targets depend on, at +varying degrees of indirection. This `_build` fixes all Deno-style `.ts` +import statements, invokes `esbuild`, and emits TypeScript declarations +via `tsc`. + +### Polyfills + +We use `esbuild` to compile to these targets. This allows us to abstract +differences at module boundaries and replace them as-needed. For example: each +of Node, Deno, and the Browser have different WASI libraries with slightly different +interfaces. We define a **virtual module**, `js-sdk:wasi`, and implement it by: + +1. Modifying `deno.json`; adding a mapping from `js-sdk:wasi` to `./src/polyfills/deno:wasi.ts`. +2. Adding a `types/js-sdk:wasi/index.d.ts` file. +3. Modifying the esbuild `alias` added by `build_worker`, `build_worker_node`, + `build_node_cjs`, `build_node_esm`, and `build_browser`. + - Node overrides are set to `./src/polyfills/node:wasi.ts`. + - Browser overrides are set to `./src/polyfills/browser:wasi.ts`. + +In this manner, differences between the platforms are hidden and the core of +the library can be written in "mutually intelligble" TypeScript. + +One notable exception to this rule: Deno implements Node polyfills; for +complicated imports, like `node:worker_threads`, we instead only polyfill the +browser. The browser polyfill is split into `host:node:worker_threads.ts` and +`worker:node:worker_threads.ts`: these polyfill just enough of the Node worker +thread API over the top of builtin workers to make them adhere to the same +interface. + +### Testing + +Tests are co-located with source code, using the `*.test.ts` pattern. Tests +are run in three forms: + +- Interpreted, via `deno test -A` +- Compiled, via `node --test` +- And via playwright, which polyfills `node:test` using `tape` and runs tests + across firefox, webkit, and chromium. + +The `assert` API is polyfilled in browser using +[`rollup-plugin-polyfill-node`](https://npm.im/rollup-plugin-polyfill-node). +This polyfill doesn't track Node's APIs very closely, so it's best to stick to +simple assertions (`assert.equal`.) + +## The Extism runtime, shared memory, and worker threads + +This SDK defaults to running on background threads in contexts where that is +feasible. Host functions require this library to share memory between the main +and worker threads, however. The rules on transferring buffers are as follows: + +- ArrayBuffers may be transferred asynchronously between main and worker threads. Once + transferred they may no longer be accessed on the sending thread. +- SharedArrayBuffers may be sent _only_ from the main thread. (All browsers allow + the creation of SharedArrayBuffers off of the main thread, but Chromium disallows + _sending_ those SharedArrayBuffers to the main thread from the worker.) +- Browser environments disallow using `TextDecoder` against typed arrays backed by + SharedArrayBuffers. The Extism library handles this transparently by copying out + of shared memory. + +These rules make navigating memory sharing fairly tricky compared to other SDK platforms. +As a result, the JS SDK includes its own extism runtime, which: + +- Reserves 16 bits of address space for "page id" information, leaving 48 bits per "page" + of allocated memory. +- Creates sharedarraybuffers on the worker thread and shares them with the worker thread on + `call()`. +- Worker-originated pages are transferred up to the main thread and copied into sharedarraybuffers + whenever the worker transfers control to the main thread (whether returning from a `call()` or + calling a `hostfn`.) +- When new pages are created during the execution of a `hostfn`, they will be + _copied down_ to the worker thread using a 64KiB scratch space. diff --git a/Makefile b/Makefile deleted file mode 100644 index dbb4807..0000000 --- a/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -.PHONY: test - -prepare: - npm install - -build: - npm run build - -test: prepare - npm run test - deno test -A .\tests\mod.test.ts - -clean: - echo "No clean implemented" - -publish: clean prepare build - npm publish - -format: - npx prettier --write src - -lint: - npx prettier --check src - -docs: - npx typedoc --out doc src - -show-docs: docs - open doc/index.html - -update-kernel: - node update-kernel.js \ No newline at end of file diff --git a/README.md b/README.md index 3109f36..1b92f4c 100644 --- a/README.md +++ b/README.md @@ -44,19 +44,17 @@ The primary concept in Extism is the [plug-in](https://extism.org/docs/concepts/ Plug-in code can come from a file on disk, object storage or any number of places. Since you may not have one handy let's load a demo plug-in from the web: ```js -const wasm = { - url: 'https://github.com/extism/plugins/releases/latest/download/count_vowels.wasm' -} - -const plugin = await createPlugin(wasm, { - // NOTE: If you get an error like "TypeError: WebAssembly.instantiate(): Import #0 module="wasi_snapshot_preview1": module is not an object or function", then your plugin requires WASI support - useWasi: true, +const plugin = await createPlugin( + 'https://github.com/extism/plugins/releases/latest/download/count_vowels.wasm', + { useWasi: true } }); ``` ## Calling A Plug-in's Exports -This plug-in was written in Rust and it does one thing, it counts vowels in a string. As such, it exposes one "export" function: `count_vowels`. We can call exports using `ExtismPlugin.call`: +This plug-in was written in Rust and it does one thing, it counts vowels in a +string. As such, it exposes one "export" function: `count_vowels`. We can call +exports using `ExtismPlugin.call`: ```js let out = await plugin.call("count_vowels", new TextEncoder().encode(input)); @@ -65,11 +63,16 @@ console.log(new TextDecoder().decode(out.buffer)) // => {"count": 3, "total": 3, "vowels": "aeiouAEIOU"} ``` -All exports have a simple interface of optional bytes in, and optional bytes out. This plug-in happens to take a string and return a JSON encoded string with a report of results. +All exports have a simple interface of optional bytes in, and optional bytes +out. This plug-in happens to take a string and return a JSON encoded string +with a report of results. ### Plug-in State -Plug-ins may be stateful or stateless. Plug-ins can maintain state b/w calls by the use of variables. Our count vowels plug-in remembers the total number of vowels it's ever counted in the "total" key in the result. You can see this by making subsequent calls to the export: +Plug-ins may be stateful or stateless. Plug-ins can maintain state b/w calls by +the use of variables. Our count vowels plug-in remembers the total number of +vowels it's ever counted in the "total" key in the result. You can see this by +making subsequent calls to the export: ```js let out = await plugin.call("count_vowels", new TextEncoder().encode("Hello, World!")); @@ -86,7 +89,9 @@ These variables will persist until this plug-in is freed or you initialize a new ### Configuration -Plug-ins may optionally take a configuration object. This is a static way to configure the plug-in. Our count-vowels plugin takes an optional configuration to change out which characters are considered vowels. Example: +Plug-ins may optionally take a configuration object. This is a static way to +configure the plug-in. Our count-vowels plugin takes an optional configuration +to change out which characters are considered vowels. Example: ```js const wasm = { @@ -113,13 +118,20 @@ console.log(new TextDecoder().decode(out.buffer)) ### Host Functions -Let's extend our count-vowels example a little bit: Instead of storing the `total` in an ephemeral plug-in var, let's store it in a persistent key-value store! +Let's extend our count-vowels example a little bit: Instead of storing the +`total` in an ephemeral plug-in var, let's store it in a persistent key-value +store! -Wasm can't use our KV store on it's own. This is where [Host Functions](https://extism.org/docs/concepts/host-functions) come in. +Wasm can't use our KV store on it's own. This is where [Host +Functions](https://extism.org/docs/concepts/host-functions) come in. -[Host functions](https://extism.org/docs/concepts/host-functions) allow us to grant new capabilities to our plug-ins from our application. They are simply some JS functions you write which can be passed down and invoked from any language inside the plug-in. +[Host functions](https://extism.org/docs/concepts/host-functions) allow us to +grant new capabilities to our plug-ins from our application. They are simply +some JS functions you write which can be passed down and invoked from any +language inside the plug-in. -Let's load the manifest like usual but load up this `count_vowels_kvstore` plug-in: +Let's load the manifest like usual but load up this `count_vowels_kvstore` +plug-in: ```js const wasm = { @@ -159,9 +171,12 @@ const options = { }; ``` -> *Note*: In order to write host functions you should get familiar with the methods on the `CurrentPlugin` type. `this` is bound to an instance of `CurrentPlugin`. +> *Note*: In order to write host functions you should get familiar with the +> methods on the `CurrentPlugin` type. `this` is bound to an instance of +> `CurrentPlugin`. -We need to pass these imports to the plug-in to create them. All imports of a plug-in must be satisfied for it to be initialized: +We need to pass these imports to the plug-in to create them. All imports of a +plug-in must be satisfied for it to be initialized: ```js const plugin = await createPlugin(wasm, options); @@ -194,11 +209,3 @@ deno run -A ./examples/deno.ts ./wasm/config.wasm bun run ./examples/node.js wasm/config.wasm ``` - -## Update `extism-kernel.wasm`: - -We are shipping an embedded kernel in base64 form in plugin.ts. To update it, you can run: - -``` -make update-kernel -``` diff --git a/build.js b/build.js deleted file mode 100644 index 82d9817..0000000 --- a/build.js +++ /dev/null @@ -1,28 +0,0 @@ -const { build } = require("esbuild"); -const { peerDependencies } = require('./package.json') - -const sharedConfig = { - bundle: true, - minify: true, - drop: [], // preseve debugger statements - external: Object.keys(peerDependencies || {}), -}; - -// NodeJS CSJ -build({ - ...sharedConfig, - entryPoints: ["src/node/index.ts"], - platform: 'node', // for CJS - outfile: "dist/node/index.js", - external: [ './src/mod.ts', "sync-fetch", "child_process" ] -}); - -// Browser ESM -build({ - ...sharedConfig, - entryPoints: ["src/browser/index.ts"], - outfile: "dist/browser/index.mjs", - platform: 'neutral', - external: [ './src/mod.ts', "sync-fetch", "child_process" ], - format: "esm", -}); \ No newline at end of file diff --git a/deno.json b/deno.json new file mode 100644 index 0000000..7be6989 --- /dev/null +++ b/deno.json @@ -0,0 +1,8 @@ +{ + "imports": { + "js-sdk:worker-url": "./src/worker-url.ts", + "js-sdk:features": "./src/polyfills/deno:features.ts", + "js-sdk:wasi": "./src/polyfills/deno:wasi.ts", + "js-sdk:fs": "./src/polyfills/node:fs.ts" + } +} diff --git a/deno.lock b/deno.lock new file mode 100644 index 0000000..ed538a2 --- /dev/null +++ b/deno.lock @@ -0,0 +1,24 @@ +{ + "version": "3", + "packages": { + "specifiers": { + "npm:@types/node": "npm:@types/node@18.16.19" + }, + "npm": { + "@types/node@18.16.19": { + "integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==", + "dependencies": {} + } + } + }, + "remote": { + "https://deno.land/std@0.200.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", + "https://deno.land/std@0.200.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", + "https://deno.land/std@0.200.0/path/_relative.ts": "27bdeffb5311a47d85be26d37ad1969979359f7636c5cd9fcf05dcd0d5099dc5", + "https://deno.land/std@0.200.0/path/_resolve.ts": "7a3616f1093735ed327e758313b79c3c04ea921808ca5f19ddf240cb68d0adf6", + "https://deno.land/std@0.200.0/path/_util.ts": "4e191b1bac6b3bf0c31aab42e5ca2e01a86ab5a0d2e08b75acf8585047a86221", + "https://deno.land/std@0.200.0/path/relative.ts": "7db80c5035016174267da16321a742d76e875215c317859a383b12f413c6f5d6", + "https://deno.land/std@0.200.0/path/resolve.ts": "103b62207726a27f28177f397008545804ecb20aaf00623af1f622b18cd80b9f", + "https://deno.land/std@0.200.0/wasi/snapshot_preview1.ts": "25c18c35df4ba5755b059d3e178f3478ff210dc7fe7baef12e495cf2ed0a8b13" + } +} diff --git a/examples/deno.ts b/examples/deno.ts old mode 100644 new mode 100755 index 5f061e7..88b6c64 --- a/examples/deno.ts +++ b/examples/deno.ts @@ -1,13 +1,11 @@ -import createPlugin from '../src/deno/mod.ts' +#!/usr/bin/env deno run -A +import createPlugin from '../src/mod.ts' const filename = Deno.args[0] || "wasm/hello.wasm"; const funcname = Deno.args[1] || "run_test"; const input = Deno.args[2] || "this is a test"; -const wasm = { - url: filename -} -const plugin = await createPlugin(wasm, { +const plugin = await createPlugin(filename, { useWasi: true, config: { "thing": "testing" @@ -16,4 +14,6 @@ const plugin = await createPlugin(wasm, { const res = await plugin.call(funcname, new TextEncoder().encode(input)); const s = new TextDecoder().decode(res.buffer); -console.log(s) \ No newline at end of file +console.log(s) + +await plugin.close() diff --git a/examples/index.html b/examples/index.html index dc27dff..9057bfd 100644 --- a/examples/index.html +++ b/examples/index.html @@ -39,7 +39,7 @@ - + @@ -47,7 +47,7 @@ - + @@ -55,10 +55,8 @@ - - ' > dist/{{ out }}/index.html + +build: prepare build_worker_node build_worker_browser build_browser build_node_esm build_node_cjs _build_browser_tests _build_node_tests + npm pack --pack-destination dist/ + +test: build && test-artifacts + #!/bin/bash + set -eou pipefail + just serve 8124 false & + cleanup() { + &>/dev/null curl http://localhost:8124/quit + } + trap cleanup EXIT + trap cleanup ERR + + sleep 0.5 + deno test -A src/mod.test.ts + node --no-warnings --test dist/tests/cjs/*.test.js + node --no-warnings --test dist/tests/esm/*.test.js + playwright test --browser all tests/playwright.test.js + +test-artifacts: + #!/bin/bash + set -eou pipefail + rm -rf tests/artifacts + mkdir -p tests/artifacts + cd tests/artifacts + npm init --yes + npm i ../../dist/extism*.tgz + node --no-warnings < { + console.error(err) + process.exit(1) + }) + EOF + + node --input-type=module --no-warnings < { + let statusCode = 200 + const headers = { + 'Access-Control-Allow-Origin': '*', + 'Cross-Origin-Embedder-Policy': 'require-corp', + 'Cross-Origin-Opener-Policy': 'same-origin' + } + + const url = new URL(req.url, 'http://localhost:{{port}}'); + + if (url.pathname === '/quit') { + server.close() + } + + let body = await fs.readFile(url.pathname.slice(1)).catch(err => { + if (err.code === 'EISDIR') { + url.pathname = path.join(url.pathname, 'index.html') + return fs.readFile(url.pathname.slice(1)) + } + return null + }).catch(() => null); + + if (!body) { + headers['content-type'] = 'text/html' + statusCode = 404 + body = 'not here sorry' + } else switch (path.extname(url.pathname)) { + case '.html': headers['content-type'] = 'text/html'; break + case '.wasm': headers['content-type'] = 'application/wasm'; break + case '.json': headers['content-type'] = 'application/json'; break + case '.js': headers['content-type'] = 'text/javascript'; break + } + + if ({{ logs }}) { + console.log(statusCode, url.pathname, body.length) + } + + headers['content-length'] = body.length + res.writeHead(statusCode, headers); + res.end(body); + }); diff --git a/package-lock.json b/package-lock.json index f5e0534..3cad289 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8252 +8,3572 @@ "name": "@extism/extism", "version": "1.0.0-rc1", "license": "BSD-3-Clause", - "dependencies": { - "@bjorn3/browser_wasi_shim": "^0.2.14", - "cross-env": "^7.0.3", - "crypto": "^1.0.1", - "minimatch": "^9.0.3", - "sync-fetch": "^0.5.2" - }, "devDependencies": { - "@types/jest": "^29.2.2", - "@types/sync-fetch": "^0.4.0", + "@bjorn3/browser_wasi_shim": "^0.2.14", + "@playwright/test": "^1.39.0", + "@types/node": "^20.8.7", + "@typescript-eslint/eslint-plugin": "^6.8.0", + "@typescript-eslint/parser": "^6.8.0", "esbuild": "^0.15.13", - "esbuild-jest": "^0.5.0", - "jest": "^29.6.4", - "jest-environment-jsdom": "^29.3.1", + "esbuild-node-builtin": "^0.1.1", + "eslint": "^8.51.0", + "just-install": "^1.0.11", + "playwright": "^1.39.0", "prettier": "^2.7.1", - "ts-jest": "^29.0.3", - "tslint": "^6.1.3", - "tslint-config-prettier": "^1.18.0", - "typedoc": "^0.23.20", - "typescript": "^4.8.4" + "tape": "^5.7.1", + "typedoc": "^0.25.2", + "typescript": "^5.2.2" } }, - "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, "engines": { - "node": ">=6.0.0" + "node": ">=0.10.0" } }, - "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "node_modules/@bjorn3/browser_wasi_shim": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/@bjorn3/browser_wasi_shim/-/browser_wasi_shim-0.2.14.tgz", + "integrity": "sha512-1S0NNBgFqkogICDL7DOYmXLvxv4tlrjNZps6XNB7P8rr3XwXG1DqtfgYImERUzz71GirexUVrD/jQZht7swCzw==", + "dev": true + }, + "node_modules/@esbuild/android-arm": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz", + "integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=6.9.0" + "node": ">=12" } }, - "node_modules/@babel/compat-data": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", - "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", + "node_modules/@esbuild/linux-loong64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz", + "integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==", + "cpu": [ + "loong64" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=12" } }, - "node_modules/@babel/core": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.11.tgz", - "integrity": "sha512-lh7RJrtPdhibbxndr6/xx0w8+CVlY5FJZiaSz908Fpy+G0xkBFTvwLcKJFF4PJxVfGhVWNebikpWGnOoC71juQ==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-compilation-targets": "^7.22.10", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.11", - "@babel/parser": "^7.22.11", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.11", - "@babel/types": "^7.22.11", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@babel/generator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", - "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", + "node_modules/@eslint-community/regexpp": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.1.tgz", + "integrity": "sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==", "dev": true, - "dependencies": { - "@babel/types": "^7.22.10", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, "engines": { - "node": ">=6.9.0" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz", - "integrity": "sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.5", - "browserslist": "^4.21.9", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" + "node_modules/@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@eslint/eslintrc/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, - "engines": { - "node": ">=6.9.0" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "type-fest": "^0.20.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "argparse": "^2.0.1" }, - "engines": { - "node": ">=6.9.0" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "node_modules/@eslint/eslintrc/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, "dependencies": { - "@babel/types": "^7.22.5" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=6.9.0" + "node": "*" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", - "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.5" - }, "engines": { - "node": ">=6.9.0" + "node": ">=10" }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "node_modules/@eslint/js": { + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz", + "integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==", "dev": true, "engines": { - "node": ">=6.9.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", + "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" }, "engines": { - "node": ">=6.9.0" + "node": ">=10.10.0" } }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "node_modules/@humanwhocodes/config-array/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, "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "node_modules/@humanwhocodes/config-array/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { - "node": ">=6.9.0" + "node": "*" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "engines": { - "node": ">=6.9.0" + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@ljharb/resumer": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@ljharb/resumer/-/resumer-0.0.1.tgz", + "integrity": "sha512-skQiAOrCfO7vRTq53cxznMpks7wS1va95UCidALlOVWqvBAzwPVErwizDwoMqNVMEn1mDq0utxZd02eIrvF1lw==", "dev": true, + "dependencies": { + "@ljharb/through": "^2.3.9" + }, "engines": { - "node": ">=6.9.0" + "node": ">= 0.4" } }, - "node_modules/@babel/helpers": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.11.tgz", - "integrity": "sha512-vyOXC8PBWaGc5h7GMsNx68OH33cypkEDJCHvYVVgVbbxJDROYVtexSk0gK5iCF1xNjRIN2s8ai7hwkWDq5szWg==", + "node_modules/@ljharb/through": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.11.tgz", + "integrity": "sha512-ccfcIDlogiXNq5KcbAwbaO7lMh3Tm1i3khMPYpxlK8hH/W53zN81KM9coerRLOnTGu3nfXIniAmQbRI9OxbC0w==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.11", - "@babel/types": "^7.22.11" + "call-bind": "^1.0.2" }, "engines": { - "node": ">=6.9.0" + "node": ">= 0.4" } }, - "node_modules/@babel/highlight": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", - "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", + "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, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" }, "engines": { - "node": ">=6.9.0" + "node": ">= 8" } }, - "node_modules/@babel/parser": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.13.tgz", - "integrity": "sha512-3l6+4YOvc9wx7VlCSw4yQfcBo01ECA8TicQfbnCPuCEpRQrf+gTUyGdxNw+pyTUyywp6JRD1w0YQs9TpBXYlkw==", + "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, - "bin": { - "parser": "bin/babel-parser.js" - }, "engines": { - "node": ">=6.0.0" + "node": ">= 8" } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "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, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">= 8" } }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "node_modules/@playwright/test": { + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.39.0.tgz", + "integrity": "sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "playwright": "1.39.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" } }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "node_modules/@rollup/plugin-inject": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz", + "integrity": "sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@rollup/pluginutils": "^5.0.1", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "node_modules/@rollup/pluginutils": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.5.tgz", + "integrity": "sha512-6aEYR910NyP73oHiJglti74iRyOwgFU4x3meH/H8OJx6Ry0j6cOVZ5X/wTvub7G7Ao6qaHBEaNsV3GLJkSsF+Q==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "node_modules/@types/estree": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.3.tgz", + "integrity": "sha512-CS2rOaoQ/eAgAfcTfq6amKG7bsN+EMcgGY4FAFQdvSj2y1ixvOZTUA9mOtCai7E1SYu283XNw7urKK30nP3wkQ==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", + "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.8.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.7.tgz", + "integrity": "sha512-21TKHHh3eUHIi2MloeptJWALuCu5H7HQTdTrWIFReA8ad+aggoX+lRes3ex7/FtpC+sVUpFMQ+QTfYr74mruiQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "undici-types": "~5.25.1" } }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "node_modules/@types/semver": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.8.0.tgz", + "integrity": "sha512-GosF4238Tkes2SHPQ1i8f6rMtG6zlKwMEB0abqSJ3Npvos+doIlc/ATG+vX1G9coDF3Ex78zM3heXHLyWEwLUw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.8.0", + "@typescript-eslint/type-utils": "6.8.0", + "@typescript-eslint/utils": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": ">=6.9.0" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "node_modules/@typescript-eslint/eslint-plugin/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, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "yallist": "^4.0.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=10" } }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "node_modules/@typescript-eslint/eslint-plugin/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, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "lru-cache": "^6.0.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "node_modules/@typescript-eslint/eslint-plugin/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 + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.8.0.tgz", + "integrity": "sha512-5tNs6Bw0j6BdWuP8Fx+VH4G9fEPDxnVI7yH1IAPkQH5RUtvKwRoqdecAPdQXv4rSOADAaz1LFBZvZG7VbXivSg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" + "@typescript-eslint/scope-manager": "6.8.0", + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/typescript-estree": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.8.0.tgz", + "integrity": "sha512-xe0HNBVwCph7rak+ZHcFD6A+q50SMsFwcmfdjs9Kz4qDh5hWhaPhFjRs/SODEhroBI5Ruyvyz9LfwUJ624O40g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "node_modules/@typescript-eslint/type-utils": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.8.0.tgz", + "integrity": "sha512-RYOJdlkTJIXW7GSldUIHqc/Hkto8E+fZN96dMIFhuTJcQwdRoGN2rEWA8U6oXbLo0qufH7NPElUb+MceHtz54g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "@typescript-eslint/typescript-estree": "6.8.0", + "@typescript-eslint/utils": "6.8.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "node_modules/@typescript-eslint/types": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.8.0.tgz", + "integrity": "sha512-p5qOxSum7W3k+llc7owEStXlGmSl8FcGvhYt8Vjy7FqEnmkCVlM3P57XQEGj58oqaBWDQXbJDZxwUWMS/EAPNQ==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" + "engines": { + "node": "^16.0.0 || >=18.0.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.8.0.tgz", + "integrity": "sha512-ISgV0lQ8XgW+mvv5My/+iTUdRmGspducmQcDw5JxznasXNnZn3SKNrTRuMsEXv+V/O+Lw9AGcQCfVaOPCAk/Zg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/visitor-keys": "6.8.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": ">=6.9.0" + "node": "^16.0.0 || >=18.0.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "node_modules/@typescript-eslint/typescript-estree/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, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "yallist": "^4.0.0" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=10" } }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.11.tgz", - "integrity": "sha512-o2+bg7GDS60cJMgz9jWqRUsWkMzLCxp+jFDeDUT5sjRlAxcJWZ2ylNdI7QQ2+CH5hWu7OnN+Cv3htt7AkSf96g==", + "node_modules/@typescript-eslint/typescript-estree/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, "dependencies": { - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" + "lru-cache": "^6.0.0" }, - "engines": { - "node": ">=6.9.0" + "bin": { + "semver": "bin/semver.js" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=10" } }, - "node_modules/@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "node_modules/@typescript-eslint/typescript-estree/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 + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.8.0.tgz", + "integrity": "sha512-dKs1itdE2qFG4jr0dlYLQVppqTE+Itt7GmIf/vX6CSvsW+3ov8PbWauVKyyfNngokhIO9sKZeRGCUo1+N7U98Q==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.8.0", + "@typescript-eslint/types": "6.8.0", + "@typescript-eslint/typescript-estree": "6.8.0", + "semver": "^7.5.4" }, "engines": { - "node": ">=6.9.0" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" } }, - "node_modules/@babel/traverse": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.11.tgz", - "integrity": "sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==", + "node_modules/@typescript-eslint/utils/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, "dependencies": { - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.11", - "@babel/types": "^7.22.11", - "debug": "^4.1.0", - "globals": "^11.1.0" + "yallist": "^4.0.0" }, "engines": { - "node": ">=6.9.0" + "node": ">=10" } }, - "node_modules/@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", + "node_modules/@typescript-eslint/utils/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, "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=6.9.0" + "node": ">=10" } }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@bjorn3/browser_wasi_shim": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/@bjorn3/browser_wasi_shim/-/browser_wasi_shim-0.2.14.tgz", - "integrity": "sha512-1S0NNBgFqkogICDL7DOYmXLvxv4tlrjNZps6XNB7P8rr3XwXG1DqtfgYImERUzz71GirexUVrD/jQZht7swCzw==" + "node_modules/@typescript-eslint/utils/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 }, - "node_modules/@cnakazawa/watch": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", - "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.8.0.tgz", + "integrity": "sha512-oqAnbA7c+pgOhW2OhGvxm0t1BULX5peQI/rLsNDpGM78EebV3C9IGbX5HNZabuZ6UQrYveCLjKo8Iy/lLlBkkg==", "dev": true, "dependencies": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" - }, - "bin": { - "watch": "cli.js" + "@typescript-eslint/types": "6.8.0", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": ">=0.1.95" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz", - "integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==", - "cpu": [ - "arm" - ], + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", "dev": true, - "optional": true, - "os": [ - "android" - ], + "bin": { + "acorn": "bin/acorn" + }, "engines": { - "node": ">=12" + "node": ">=0.4.0" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz", - "integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==", - "cpu": [ - "loong64" - ], + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">=8" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/@jest/console": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.4.tgz", - "integrity": "sha512-wNK6gC0Ha9QeEPSkeJedQuTQqxZYnDPuDcDhVuVatRvMkL4D0VTvFVZj+Yuh6caG2aOfzkUZ36KtCmLNtR02hw==", + "node_modules/ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", + "dev": true + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", "dev": true, "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3", - "slash": "^3.0.0" + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/@jest/console/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/array.prototype.every": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/array.prototype.every/-/array.prototype.every-1.1.5.tgz", + "integrity": "sha512-FfMQJ+/joFGXpRCltbzV3znaP5QxIhLFySo0fEPn3GuoYlud9LhknMCIxdYKC2qsM/6VHoSp6YGwe3EZXrEcwQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "is-string": "^1.0.7" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@jest/console/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@jest/console/node_modules/color-convert": { + "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 + }, + "node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=8" } }, - "node_modules/@jest/console/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/@jest/console/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==", + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/@jest/console/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "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 + }, + "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, "dependencies": { - "has-flag": "^4.0.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">=8" + "node": ">= 8" } }, - "node_modules/@jest/core": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.4.tgz", - "integrity": "sha512-U/vq5ccNTSVgYH7mHnodHmCffGWHJnz/E1BEWlLuK5pM4FZmGfBn/nrJGLjUsSmyx3otCeqc1T31F4y08AMDLg==", + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { - "@jest/console": "^29.6.4", - "@jest/reporters": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.6.3", - "jest-config": "^29.6.4", - "jest-haste-map": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-resolve-dependencies": "^29.6.4", - "jest-runner": "^29.6.4", - "jest-runtime": "^29.6.4", - "jest-snapshot": "^29.6.4", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", - "jest-watcher": "^29.6.4", - "micromatch": "^4.0.4", - "pretty-format": "^29.6.3", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "ms": "2.1.2" }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "engines": { + "node": ">=6.0" }, "peerDependenciesMeta": { - "node-notifier": { + "supports-color": { "optional": true } } }, - "node_modules/@jest/core/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" + "node_modules/deep-equal": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.2.tgz", + "integrity": "sha512-xjVyBf0w5vH0I42jdAZzOKVldmPgSulmiyPRywoyq7HXC9qdgo17kxJE+rdnif5Tz6+pIrpJI8dCpMNLIGkUiA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.1", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.0", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@jest/core/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/deep-equal/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">= 0.4" } }, - "node_modules/@jest/core/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { - "node": ">=7.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@jest/core/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/core/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==", + "node_modules/defined": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", + "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", "dev": true, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@jest/core/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "path-type": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/@jest/environment": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.4.tgz", - "integrity": "sha512-sQ0SULEjA1XUTHmkBRl7A1dyITM9yb1yb3ZNKPX3KlTd6IG7mWUe3e2yfExtC2Zz1Q+mMckOLHmL/qLiuQJrBQ==", + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "dependencies": { - "@jest/fake-timers": "^29.6.4", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.6.3" + "esutils": "^2.0.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.0.0" } }, - "node_modules/@jest/expect": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.4.tgz", - "integrity": "sha512-Warhsa7d23+3X5bLbrbYvaehcgX5TLYhI03JKoedTiI8uJU4IhqYBWF7OSSgUyz4IgLpUYPkK0AehA5/fRclAA==", + "node_modules/dotignore": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dotignore/-/dotignore-0.1.2.tgz", + "integrity": "sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==", "dev": true, "dependencies": { - "expect": "^29.6.4", - "jest-snapshot": "^29.6.4" + "minimatch": "^3.0.4" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "bin": { + "ignored": "bin/ignored" } }, - "node_modules/@jest/expect-utils": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.4.tgz", - "integrity": "sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg==", + "node_modules/dotignore/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, "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@jest/fake-timers": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.4.tgz", - "integrity": "sha512-6UkCwzoBK60edXIIWb0/KWkuj7R7Qq91vVInOe3De6DSpaEiqjKcJw4F7XUet24Wupahj9J6PlR09JqJ5ySDHw==", + "node_modules/dotignore/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, "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.6.3", - "jest-mock": "^29.6.3", - "jest-util": "^29.6.3" + "brace-expansion": "^1.1.7" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "*" } }, - "node_modules/@jest/globals": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.4.tgz", - "integrity": "sha512-wVIn5bdtjlChhXAzVXavcY/3PEjf4VqM174BM3eGL5kMxLiZD5CLnbmkEyA1Dwh9q8XjP6E8RwjBsY/iCWrWsA==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/expect": "^29.6.4", - "@jest/types": "^29.6.3", - "jest-mock": "^29.6.3" + "node_modules/es-abstract": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz", + "integrity": "sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.1", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.12", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.11" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@jest/reporters": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.4.tgz", - "integrity": "sha512-sxUjWxm7QdchdrD3NfWKrL8FBsortZeibSJv4XLjESOOjSUOkjQcb0ZHJwfhEGIvBvTluTzfG2yZWZhkrXJu8g==", + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", "dev": true, "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3", - "jest-worker": "^29.6.4", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@jest/reporters/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/es-get-iterator/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 0.4" } }, - "node_modules/@jest/reporters/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@jest/reporters/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/esbuild": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz", + "integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==", "dev": true, - "dependencies": { - "color-name": "~1.1.4" + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": ">=7.0.0" + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.15.18", + "@esbuild/linux-loong64": "0.15.18", + "esbuild-android-64": "0.15.18", + "esbuild-android-arm64": "0.15.18", + "esbuild-darwin-64": "0.15.18", + "esbuild-darwin-arm64": "0.15.18", + "esbuild-freebsd-64": "0.15.18", + "esbuild-freebsd-arm64": "0.15.18", + "esbuild-linux-32": "0.15.18", + "esbuild-linux-64": "0.15.18", + "esbuild-linux-arm": "0.15.18", + "esbuild-linux-arm64": "0.15.18", + "esbuild-linux-mips64le": "0.15.18", + "esbuild-linux-ppc64le": "0.15.18", + "esbuild-linux-riscv64": "0.15.18", + "esbuild-linux-s390x": "0.15.18", + "esbuild-netbsd-64": "0.15.18", + "esbuild-openbsd-64": "0.15.18", + "esbuild-sunos-64": "0.15.18", + "esbuild-windows-32": "0.15.18", + "esbuild-windows-64": "0.15.18", + "esbuild-windows-arm64": "0.15.18" } }, - "node_modules/@jest/reporters/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/reporters/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==", + "node_modules/esbuild-android-64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz", + "integrity": "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", - "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", + "node_modules/esbuild-android-arm64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz", + "integrity": "sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/@jest/reporters/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==", + "node_modules/esbuild-darwin-64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz", + "integrity": "sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/@jest/reporters/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==", + "node_modules/esbuild-darwin-arm64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz", + "integrity": "sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/@jest/reporters/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/esbuild-freebsd-64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz", + "integrity": "sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/@jest/reporters/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 - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "node_modules/esbuild-freebsd-arm64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz", + "integrity": "sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=12" } }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "node_modules/esbuild-linux-32": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz", + "integrity": "sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==", + "cpu": [ + "ia32" + ], "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=12" } }, - "node_modules/@jest/test-result": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.4.tgz", - "integrity": "sha512-uQ1C0AUEN90/dsyEirgMLlouROgSY+Wc/JanVVk0OiUKa5UFh7sJpMEM3aoUBAz2BRNvUJ8j3d294WFuRxSyOQ==", + "node_modules/esbuild-linux-64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz", + "integrity": "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@jest/console": "^29.6.4", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=12" } }, - "node_modules/@jest/test-sequencer": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.4.tgz", - "integrity": "sha512-E84M6LbpcRq3fT4ckfKs9ryVanwkaIB0Ws9bw3/yP4seRLg/VaCZ/LgW0MCq5wwk4/iP/qnilD41aj2fsw2RMg==", + "node_modules/esbuild-linux-arm": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz", + "integrity": "sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "@jest/test-result": "^29.6.4", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", - "slash": "^3.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=12" } }, - "node_modules/@jest/transform": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.4.tgz", - "integrity": "sha512-8thgRSiXUqtr/pPGY/OsyHuMjGyhVnWrFAwoxmIemlBuiMyU1WFs0tXoNxzcr4A4uErs/ABre76SGmrr5ab/AA==", + "node_modules/esbuild-linux-arm64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz", + "integrity": "sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.6.3", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=12" } }, - "node_modules/@jest/transform/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/esbuild-linux-mips64le": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz", + "integrity": "sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==", + "cpu": [ + "mips64el" + ], "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/transform/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/transform/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/transform/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/transform/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/@jest/transform/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/transform/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/types/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/types/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/types/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/types/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/types/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", - "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", - "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.4", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.4.tgz", - "integrity": "sha512-PhglGmhWeD46FYOVLt3X7TiWjzwuVGW9wG/4qocPevXMjCmrIc5b6db9WjeGE4QYVpUAWMDv3v0IiBwObY289A==", - "dev": true, - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/jsdom": { - "version": "20.0.1", - "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", - "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/tough-cookie": "*", - "parse5": "^7.0.0" - } - }, - "node_modules/@types/node": { - "version": "20.5.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.7.tgz", - "integrity": "sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==", - "dev": true - }, - "node_modules/@types/node-fetch": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz", - "integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==", - "dev": true, - "dependencies": { - "@types/node": "*", - "form-data": "^3.0.0" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "node_modules/@types/sync-fetch": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@types/sync-fetch/-/sync-fetch-0.4.0.tgz", - "integrity": "sha512-kUhMgk1Es/vKdwC27O29j2Y3KM52Z/k1c1sBUfNJoI8fr0WNRRFaMmq7oJ18xkiN08cNBsgNA86CV030JthgOA==", - "dev": true, - "dependencies": { - "@types/node-fetch": "*" - } - }, - "node_modules/@types/tough-cookie": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", - "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-globals": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", - "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", - "dev": true, - "dependencies": { - "acorn": "^8.1.0", - "acorn-walk": "^8.0.2" - } - }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-sequence-parser": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", - "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", - "dev": true - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "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, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "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, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true, - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/babel-jest": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.4.tgz", - "integrity": "sha512-meLj23UlSLddj6PC+YTOFRgDAtjnZom8w/ACsrx0gtPtv5cJZk0A5Unk5bV4wixD7XaPCN1fQvpww8czkZURmw==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.6.4", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-jest/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/babel-jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/babel-jest/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/babel-jest/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/babel-jest/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-jest/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "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==" - }, - "node_modules/base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "dependencies": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.21.10", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", - "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001517", - "electron-to-chromium": "^1.4.477", - "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.11" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "dependencies": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001524", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001524.tgz", - "integrity": "sha512-Jj917pJtYg9HSJBF95HVX3Cdr89JUyLT4IZ8SvM5aDRni95swKgYi3TgYLH5hnGfPE/U1dg6IfZ50UsIlLkwSA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", - "dev": true, - "dependencies": { - "rsvp": "^4.8.4" - }, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true - }, - "node_modules/class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "dependencies": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "node_modules/collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", - "dev": true, - "dependencies": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "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 - }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "node_modules/copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cross-env": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", - "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "bin": { - "cross-env": "src/bin/cross-env.js", - "cross-env-shell": "src/bin/cross-env-shell.js" - }, - "engines": { - "node": ">=10.14", - "npm": ">=6", - "yarn": ">=1" - } - }, - "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==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", - "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", - "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." - }, - "node_modules/cssom": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", - "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", - "dev": true - }, - "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "dependencies": { - "cssom": "~0.3.6" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - }, - "node_modules/data-urls": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", - "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", - "dev": true, - "dependencies": { - "abab": "^2.0.6", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "dev": true - }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", - "dev": true, - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/domexception": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", - "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", - "dev": true, - "dependencies": { - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.504", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.504.tgz", - "integrity": "sha512-cSMwIAd8yUh54VwitVRVvHK66QqHWE39C3DRj8SWiXitEpVSY3wNPD9y1pxQtLIi4w3UdzF9klLsmuPshz09DQ==", - "dev": true - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "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, - "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, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/esbuild": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz", - "integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.15.18", - "@esbuild/linux-loong64": "0.15.18", - "esbuild-android-64": "0.15.18", - "esbuild-android-arm64": "0.15.18", - "esbuild-darwin-64": "0.15.18", - "esbuild-darwin-arm64": "0.15.18", - "esbuild-freebsd-64": "0.15.18", - "esbuild-freebsd-arm64": "0.15.18", - "esbuild-linux-32": "0.15.18", - "esbuild-linux-64": "0.15.18", - "esbuild-linux-arm": "0.15.18", - "esbuild-linux-arm64": "0.15.18", - "esbuild-linux-mips64le": "0.15.18", - "esbuild-linux-ppc64le": "0.15.18", - "esbuild-linux-riscv64": "0.15.18", - "esbuild-linux-s390x": "0.15.18", - "esbuild-netbsd-64": "0.15.18", - "esbuild-openbsd-64": "0.15.18", - "esbuild-sunos-64": "0.15.18", - "esbuild-windows-32": "0.15.18", - "esbuild-windows-64": "0.15.18", - "esbuild-windows-arm64": "0.15.18" - } - }, - "node_modules/esbuild-android-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz", - "integrity": "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-android-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz", - "integrity": "sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-darwin-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz", - "integrity": "sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-darwin-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz", - "integrity": "sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-freebsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz", - "integrity": "sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-freebsd-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz", - "integrity": "sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-jest": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/esbuild-jest/-/esbuild-jest-0.5.0.tgz", - "integrity": "sha512-AMZZCdEpXfNVOIDvURlqYyHwC8qC1/BFjgsrOiSL1eyiIArVtHL8YAC83Shhn16cYYoAWEW17yZn0W/RJKJKHQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.17", - "@babel/plugin-transform-modules-commonjs": "^7.12.13", - "babel-jest": "^26.6.3" - }, - "peerDependencies": { - "esbuild": ">=0.8.50" - } - }, - "node_modules/esbuild-jest/node_modules/@jest/transform": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", - "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.1.0", - "@jest/types": "^26.6.2", - "babel-plugin-istanbul": "^6.0.0", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-util": "^26.6.2", - "micromatch": "^4.0.2", - "pirates": "^4.0.1", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/esbuild-jest/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/esbuild-jest/node_modules/@types/yargs": { - "version": "15.0.15", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.15.tgz", - "integrity": "sha512-IziEYMU9XoVj8hWg7k+UJrXALkGFjWJhn5QFEv9q4p+v40oZhSuC135M38st8XPjICL7Ey4TV64ferBGUoJhBg==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/esbuild-jest/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/esbuild-jest/node_modules/babel-jest": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", - "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", - "dev": true, - "dependencies": { - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/babel__core": "^7.1.7", - "babel-plugin-istanbul": "^6.0.0", - "babel-preset-jest": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "slash": "^3.0.0" - }, - "engines": { - "node": ">= 10.14.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/esbuild-jest/node_modules/babel-plugin-jest-hoist": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", - "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/esbuild-jest/node_modules/babel-preset-jest": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", - "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^26.6.2", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": ">= 10.14.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/esbuild-jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/esbuild-jest/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/esbuild-jest/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/esbuild-jest/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/esbuild-jest/node_modules/jest-haste-map": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", - "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-regex-util": "^26.0.0", - "jest-serializer": "^26.6.2", - "jest-util": "^26.6.2", - "jest-worker": "^26.6.2", - "micromatch": "^4.0.2", - "sane": "^4.0.3", - "walker": "^1.0.7" - }, - "engines": { - "node": ">= 10.14.2" - }, - "optionalDependencies": { - "fsevents": "^2.1.2" - } - }, - "node_modules/esbuild-jest/node_modules/jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", - "dev": true, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/esbuild-jest/node_modules/jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/esbuild-jest/node_modules/jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", - "dev": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/esbuild-jest/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/esbuild-jest/node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/esbuild-linux-32": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz", - "integrity": "sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz", - "integrity": "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-arm": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz", - "integrity": "sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz", - "integrity": "sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-mips64le": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz", - "integrity": "sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-ppc64le": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz", - "integrity": "sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-riscv64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz", - "integrity": "sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-linux-s390x": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz", - "integrity": "sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-netbsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz", - "integrity": "sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-openbsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz", - "integrity": "sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-sunos-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz", - "integrity": "sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-windows-32": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz", - "integrity": "sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-windows-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz", - "integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/esbuild-windows-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz", - "integrity": "sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dev": true, - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/exec-sh": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", - "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==", - "dev": true - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "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/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", - "dev": true, - "dependencies": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/expand-brackets/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/expect": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.4.tgz", - "integrity": "sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.6.4", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "dev": true, - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "dependencies": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", - "dev": true, - "dependencies": { - "map-cache": "^0.2.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "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, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "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, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob/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, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/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, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "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 - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "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, - "engines": { - "node": ">=4" - } - }, - "node_modules/has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", - "dev": true, - "dependencies": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", - "dev": true, - "dependencies": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", - "dev": true, - "dependencies": { - "whatwg-encoding": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dev": true, - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "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, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "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 - }, - "node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "node_modules/is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "dependencies": { - "ci-info": "^2.0.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "node_modules/is-ci/node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "node_modules/is-core-module": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", - "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "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, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true - }, - "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, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.4.tgz", - "integrity": "sha512-tEFhVQFF/bzoYV1YuGyzLPZ6vlPrdfvDmmAxudA1dLEuiztqg2Rkx20vkKY32xiDROcD2KXlgZ7Cu8RPeEHRKw==", - "dev": true, - "dependencies": { - "@jest/core": "^29.6.4", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.6.4" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.6.3.tgz", - "integrity": "sha512-G5wDnElqLa4/c66ma5PG9eRjE342lIbF6SUnTJi26C3J28Fv2TVY2rOyKB9YGbSA5ogwevgmxc4j4aVjrEK6Yg==", - "dev": true, - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.6.3", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.4.tgz", - "integrity": "sha512-YXNrRyntVUgDfZbjXWBMPslX1mQ8MrSG0oM/Y06j9EYubODIyHWP8hMUbjbZ19M3M+zamqEur7O80HODwACoJw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/expect": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.6.3", - "jest-matcher-utils": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-runtime": "^29.6.4", - "jest-snapshot": "^29.6.4", - "jest-util": "^29.6.3", - "p-limit": "^3.1.0", - "pretty-format": "^29.6.3", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-circus/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-circus/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-circus/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-circus/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.4.tgz", - "integrity": "sha512-+uMCQ7oizMmh8ZwRfZzKIEszFY9ksjjEQnTEMTaL7fYiL3Kw4XhqT9bYh+A4DQKUb67hZn2KbtEnDuHvcgK4pQ==", - "dev": true, - "dependencies": { - "@jest/core": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^29.6.4", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", - "prompts": "^2.0.1", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-cli/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-cli/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-cli/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-cli/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.4.tgz", - "integrity": "sha512-JWohr3i9m2cVpBumQFv2akMEnFEPVOh+9L2xIBJhJ0zOaci2ZXuKJj0tgMKQCBZAKA09H049IR4HVS/43Qb19A==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.6.4", - "@jest/types": "^29.6.3", - "babel-jest": "^29.6.4", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.6.4", - "jest-environment-node": "^29.6.4", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-runner": "^29.6.4", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.6.3", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-config/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-config/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-config/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-config/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-config/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-diff": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.4.tgz", - "integrity": "sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-diff/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-diff/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-diff/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-diff/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-docblock": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.6.3.tgz", - "integrity": "sha512-2+H+GOTQBEm2+qFSQ7Ma+BvyV+waiIFxmZF5LdpBsAEjWX8QYjSCa4FrkIYtbfXUJJJnFCYrOtt6TZ+IAiTjBQ==", - "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.3.tgz", - "integrity": "sha512-KoXfJ42k8cqbkfshW7sSHcdfnv5agDdHCPA87ZBdmHP+zJstTJc0ttQaJ/x7zK6noAL76hOuTIJ6ZkQRS5dcyg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.6.3", - "pretty-format": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-each/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-each/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-each/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-each/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-each/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-jsdom": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.6.4.tgz", - "integrity": "sha512-K6wfgUJ16DoMs02JYFid9lOsqfpoVtyJxpRlnTxUHzvZWBnnh2VNGRB9EC1Cro96TQdq5TtSjb3qUjNaJP9IyA==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/fake-timers": "^29.6.4", - "@jest/types": "^29.6.3", - "@types/jsdom": "^20.0.0", - "@types/node": "*", - "jest-mock": "^29.6.3", - "jest-util": "^29.6.3", - "jsdom": "^20.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jest-environment-node": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.4.tgz", - "integrity": "sha512-i7SbpH2dEIFGNmxGCpSc2w9cA4qVD+wfvg2ZnfQ7XVrKL0NA5uDVBIiGH8SR4F0dKEv/0qI5r+aDomDf04DpEQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/fake-timers": "^29.6.4", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.6.3", - "jest-util": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.4.tgz", - "integrity": "sha512-12Ad+VNTDHxKf7k+M65sviyynRoZYuL1/GTuhEVb8RYsNSNln71nANRb/faSyWvx0j+gHcivChXHIoMJrGYjog==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.6.3", - "jest-worker": "^29.6.4", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.3.tgz", - "integrity": "sha512-0kfbESIHXYdhAdpLsW7xdwmYhLf1BRu4AA118/OxFm0Ho1b2RcTmO4oF6aAMaxpxdxnJ3zve2rgwzNBD4Zbm7Q==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.4.tgz", - "integrity": "sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.6.4", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-matcher-utils/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-matcher-utils/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.3.tgz", - "integrity": "sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.6.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-message-util/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-message-util/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-mock": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.3.tgz", - "integrity": "sha512-Z7Gs/mOyTSR4yPsaZ72a/MtuK6RnC3JYqWONe48oLaoEcYwEDxqvbXz85G4SJrm2Z5Ar9zp6MiHF4AlFlRM4Pg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.4.tgz", - "integrity": "sha512-fPRq+0vcxsuGlG0O3gyoqGTAxasagOxEuyoxHeyxaZbc9QNek0AmJWSkhjlMG+mTsj+8knc/mWb3fXlRNVih7Q==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.4.tgz", - "integrity": "sha512-7+6eAmr1ZBF3vOAJVsfLj1QdqeXG+WYhidfLHBRZqGN24MFRIiKG20ItpLw2qRAsW/D2ZUUmCNf6irUr/v6KHA==", - "dev": true, - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.6.4" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-resolve/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-resolve/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-resolve/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-resolve/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.4.tgz", - "integrity": "sha512-SDaLrMmtVlQYDuG0iSPYLycG8P9jLI+fRm8AF/xPKhYDB2g6xDWjXBrR5M8gEWsK6KVFlebpZ4QsrxdyIX1Jaw==", - "dev": true, - "dependencies": { - "@jest/console": "^29.6.4", - "@jest/environment": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.6.3", - "jest-environment-node": "^29.6.4", - "jest-haste-map": "^29.6.4", - "jest-leak-detector": "^29.6.3", - "jest-message-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-runtime": "^29.6.4", - "jest-util": "^29.6.3", - "jest-watcher": "^29.6.4", - "jest-worker": "^29.6.4", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runner/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-runner/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-runner/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-runner/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.4.tgz", - "integrity": "sha512-s/QxMBLvmwLdchKEjcLfwzP7h+jsHvNEtxGP5P+Fl1FMaJX2jMiIqe4rJw4tFprzCwuSvVUo9bn0uj4gNRXsbA==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/fake-timers": "^29.6.4", - "@jest/globals": "^29.6.4", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-mock": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-snapshot": "^29.6.4", - "jest-util": "^29.6.3", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runtime/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-runtime/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-runtime/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-runtime/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-serializer": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", - "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", - "dev": true, - "dependencies": { - "@types/node": "*", - "graceful-fs": "^4.2.4" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-snapshot": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.4.tgz", - "integrity": "sha512-VC1N8ED7+4uboUKGIDsbvNAZb6LakgIPgAF4RSpF13dN6YaMokfRqO+BaqK4zIh6X3JffgwbzuGqDEjHm/MrvA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.6.4", - "@jest/transform": "^29.6.4", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.6.4", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.6.4", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3", - "natural-compare": "^1.4.0", - "pretty-format": "^29.6.3", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-snapshot/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-snapshot/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-snapshot/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot/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, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-snapshot/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, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-snapshot/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot/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 - }, - "node_modules/jest-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.3.tgz", - "integrity": "sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-util/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-util/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-util/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.3.tgz", - "integrity": "sha512-e7KWZcAIX+2W1o3cHfnqpGajdCs1jSM3DkXjGeLSNmCazv1EeI1ggTeK5wdZhF+7N+g44JI2Od3veojoaumlfg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-validate/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-validate/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-validate/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-validate/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.4.tgz", - "integrity": "sha512-oqUWvx6+On04ShsT00Ir9T4/FvBeEh2M9PTubgITPxDa739p4hoQweWPRGyYeaojgT0xTpZKF0Y/rSY1UgMxvQ==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.6.4", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.6.3", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-watcher/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-watcher/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-watcher/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-watcher/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-watcher/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.4.tgz", - "integrity": "sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==", - "dev": true, - "dependencies": { - "@types/node": "*", - "jest-util": "^29.6.3", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker/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, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsdom": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", - "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", - "dev": true, - "dependencies": { - "abab": "^2.0.6", - "acorn": "^8.8.1", - "acorn-globals": "^7.0.0", - "cssom": "^0.5.0", - "cssstyle": "^2.3.0", - "data-urls": "^3.0.2", - "decimal.js": "^10.4.2", - "domexception": "^4.0.0", - "escodegen": "^2.0.0", - "form-data": "^4.0.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.1", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.2", - "parse5": "^7.1.1", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.1.2", - "w3c-xmlserializer": "^4.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^2.0.0", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0", - "ws": "^8.11.0", - "xml-name-validator": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsdom/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/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, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir/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, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir/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 - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", - "dev": true, - "dependencies": { - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/marked": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", - "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", - "dev": true, - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 12" - } - }, - "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 - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" + "node": ">=12" } }, - "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==", + "node_modules/esbuild-linux-ppc64le": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz", + "integrity": "sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==", + "cpu": [ + "ppc64" + ], "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6" + "node": ">=12" } }, - "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, + "node_modules/esbuild-linux-riscv64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz", + "integrity": "sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=12" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "node_modules/esbuild-linux-s390x": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz", + "integrity": "sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==", + "cpu": [ + "s390x" + ], "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "node_modules/esbuild-netbsd-64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz", + "integrity": "sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/esbuild-node-builtin": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/esbuild-node-builtin/-/esbuild-node-builtin-0.1.1.tgz", + "integrity": "sha512-lGg3Q3UV5/BP9M83bgpswqjRQ7guPeA/4eZb94d/dNXsOQTTc8atKbJKtAouBSDZ/EAjMOBGC0RMvbaX5SFxbg==", "dev": true, "dependencies": { - "minimist": "^1.2.6" + "debug": "^4.3.4", + "rollup-plugin-polyfill-node": "^0.12.0" }, - "bin": { - "mkdirp": "bin/cmd.js" + "peerDependencies": { + "esbuild": "*" } }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "node_modules/esbuild-openbsd-64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz", + "integrity": "sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, + "node_modules/esbuild-sunos-64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz", + "integrity": "sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "node": ">=12" } }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", - "dev": true - }, - "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==", + "node_modules/esbuild-windows-32": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz", + "integrity": "sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==", + "cpu": [ + "ia32" + ], "dev": true, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "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==", + "node_modules/esbuild-windows-64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz", + "integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/nwsapi": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", - "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", - "dev": true - }, - "node_modules/object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "node_modules/esbuild-windows-arm64": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz", + "integrity": "sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "node_modules/object-copy/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "node_modules/eslint": { + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz", + "integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==", "dev": true, "dependencies": { - "is-descriptor": "^0.1.0" + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.51.0", + "@humanwhocodes/config-array": "^0.11.11", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" }, "engines": { - "node": ">=0.10.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/object-copy/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { - "kind-of": "^3.0.2" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">=0.10.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/object-copy/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, "engines": { - "node": ">=0.10.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/object-copy/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, - "node_modules/object-copy/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/eslint/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, "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "isobject": "^3.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "isobject": "^3.0.1" + "color-name": "~1.1.4" }, "engines": { - "node": ">=0.10.0" + "node": ">=7.0.0" } }, - "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==", + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "dependencies": { - "wrappy": "1" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "mimic-fn": "^2.1.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=6" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "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, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/eslint/node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", "dev": true, "dependencies": { - "yocto-queue": "^0.1.0" + "type-fest": "^0.20.2" }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-locate": { + "node_modules/eslint/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/js-yaml": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "dependencies": { - "p-limit": "^2.2.0" + "argparse": "^2.0.1" }, - "engines": { - "node": ">=8" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "dependencies": { - "p-try": "^2.0.0" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=6" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "node_modules/eslint/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { - "node": ">=6" + "node": "*" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "entities": "^4.4.0" + "has-flag": "^4.0.0" }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "engines": { "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "engines": { - "node": ">=0.10.0" - } - }, - "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==", - "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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 - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, "engines": { - "node": ">=8.6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://opencollective.com/eslint" } }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, "engines": { - "node": ">= 6" + "node": ">=0.10" } }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "dependencies": { - "find-up": "^4.0.0" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8" + "node": ">=4.0" } }, - "node_modules/posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=4.0" } }, - "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "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 + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "node": ">=0.10.0" } }, - "node_modules/pretty-format": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", - "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", + "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 + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dev": true, "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" + "@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": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=8.6.0" } }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "node_modules/fast-glob/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, "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" + "is-glob": "^4.0.1" }, "engines": { "node": ">= 6" } }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "reusify": "^1.0.4" } }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, "engines": { - "node": ">=6" + "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/pure-rand": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", - "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ] - }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, - "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "node_modules/flat-cache": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", + "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", "dev": true, "dependencies": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" }, "engines": { - "node": ">=0.10.0" + "node": ">=12.0.0" } }, - "node_modules/remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, - "node_modules/repeat-element": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", - "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "is-callable": "^1.1.3" } }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "dev": true, - "engines": { - "node": ">=0.10" - } + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "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, + "optional": true, + "os": [ + "darwin" + ], + "peer": true, "engines": { - "node": ">=0.10.0" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, - "node_modules/resolve": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", - "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "dev": true, "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" }, - "bin": { - "resolve": "bin/resolve" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", - "deprecated": "https://github.com/lydell/resolve-url#deprecated", - "dev": true - }, - "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "node_modules/get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", "dev": true, - "engines": { - "node": ">=10" + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, "engines": { - "node": ">=0.12" + "node": ">=8.0.0" } }, - "node_modules/rsvp": { - "version": "4.8.5", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", - "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, "engines": { - "node": "6.* || >= 7.*" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { - "ret": "~0.1.10" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "node_modules/sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "deprecated": "some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added", + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - }, - "bin": { - "sane": "src/cli.js" + "is-glob": "^4.0.3" }, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">=10.13.0" } }, - "node_modules/sane/node_modules/anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "node_modules/glob/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, "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/sane/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "node_modules/glob/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, "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=0.10.0" + "node": "*" } }, - "node_modules/sane/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", "dev": true, "dependencies": { - "is-extendable": "^0.1.0" + "define-properties": "^1.1.3" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sane/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" }, "engines": { - "node": ">=4.8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/sane/node_modules/execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.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" + "get-intrinsic": "^1.1.3" }, - "engines": { - "node": ">=6" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sane/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "function-bind": "^1.1.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sane/node_modules/fill-range/node_modules/extend-shallow": { + "node_modules/has-dynamic-import": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "resolved": "https://registry.npmjs.org/has-dynamic-import/-/has-dynamic-import-2.0.1.tgz", + "integrity": "sha512-X3fbtsZmwb6W7fJGR9o7x65fZoodygCrZ3TVycvghP62yYQfS0t4RS0Qcz+j5tQYUKeSWS09tHkWW6WhFV3XhQ==", "dev": true, "dependencies": { - "is-extendable": "^0.1.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sane/node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", "dev": true, "dependencies": { - "pump": "^3.0.0" + "get-intrinsic": "^1.1.1" }, - "engines": { - "node": ">=6" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sane/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sane/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sane/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", "dev": true, "dependencies": { - "is-buffer": "^1.1.5" + "has-symbols": "^1.0.2" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sane/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==", + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">= 4" } }, - "node_modules/sane/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", - "dev": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" + "node": ">=6" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/sane/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==", + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "dependencies": { - "path-key": "^2.0.0" - }, "engines": { "node": ">=4" } }, - "node_modules/sane/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==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "engines": { - "node": ">=4" + "node": ">=0.8.19" } }, - "node_modules/sane/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, - "bin": { - "semver": "bin/semver" + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" } }, - "node_modules/sane/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==", + "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 + }, + "node_modules/internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", "dev": true, "dependencies": { - "shebang-regex": "^1.0.0" + "get-intrinsic": "^1.2.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/sane/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==", + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sane/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", "dev": true, "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sane/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dev": true, "dependencies": { - "isexe": "^2.0.0" + "has-bigints": "^1.0.1" }, - "bin": { - "which": "bin/which" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, "dependencies": { - "xmlchars": "^2.2.0" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=v12.22.7" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "node_modules/is-core-module": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", "dev": true, "dependencies": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" + "has": "^1.0.3" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/set-value/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, "dependencies": { - "is-extendable": "^0.1.0" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/set-value/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "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, "engines": { "node": ">=0.10.0" } }, - "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==", + "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, "dependencies": { - "shebang-regex": "^3.0.0" + "is-extglob": "^2.1.1" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "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==", - "engines": { - "node": ">=8" + "node_modules/is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/shiki": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.3.tgz", - "integrity": "sha512-U3S/a+b0KS+UkTyMjoNojvTgrBHjgp7L6ovhFVZsXmBGnVdQ4K4U9oK0z63w538S91ATngv1vXigHCSWOwnr+g==", + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true, - "dependencies": { - "ansi-sequence-parser": "^1.1.0", - "jsonc-parser": "^3.2.0", - "vscode-oniguruma": "^1.7.0", - "vscode-textmate": "^8.0.0" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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 - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true + "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, + "engines": { + "node": ">=0.12.0" + } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, "dependencies": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "node_modules/is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", "dev": true, "dependencies": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" + "call-bind": "^1.0.2" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/snapdragon-node/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, "dependencies": { - "is-descriptor": "^1.0.0" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, "dependencies": { - "kind-of": "^3.2.0" + "has-symbols": "^1.0.2" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/snapdragon-util/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", "dev": true, "dependencies": { - "is-buffer": "^1.1.5" + "which-typed-array": "^1.1.11" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/snapdragon/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", "dev": true, - "dependencies": { - "ms": "2.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/snapdragon/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, "dependencies": { - "is-descriptor": "^0.1.0" + "call-bind": "^1.0.2" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/snapdragon/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", "dev": true, "dependencies": { - "is-extendable": "^0.1.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/snapdragon/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "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 + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, + "node_modules/just-install": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/just-install/-/just-install-1.0.11.tgz", + "integrity": "sha512-3nW53hWK9muHUTXthiTcUqFkd5FXDad7YLoDWJonVhPUZxX8PgCVGvaI8AQ5gqJZ45MBd/hdqg5XPIWPh9Bk/g==", "dev": true, - "dependencies": { - "kind-of": "^3.0.2" + "hasInstallScript": true, + "bin": { + "just": "bin/just" }, "engines": { - "node": ">=0.10.0" + "node": ">=8.5" } }, - "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "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, "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" + "json-buffer": "3.0.1" } }, - "node_modules/snapdragon/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "dependencies": { - "kind-of": "^3.0.2" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8.0" } }, - "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, + "node_modules/magic-string": { + "version": "0.30.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", + "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", "dev": true, "dependencies": { - "is-buffer": "^1.1.5" + "@jridgewell/sourcemap-codec": "^1.4.15" }, "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "node_modules/snapdragon/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true, - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "bin": { + "marked": "bin/marked.js" }, "engines": { - "node": ">=0.10.0" + "node": ">= 12" } }, - "node_modules/snapdragon/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "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, "engines": { - "node": ">=0.10.0" + "node": ">= 8" } }, - "node_modules/snapdragon/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8.6" } }, - "node_modules/snapdragon/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/snapdragon/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "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==", + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "node_modules/mock-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mock-property/-/mock-property-1.0.2.tgz", + "integrity": "sha512-GHVKHd3bFiXtvZtp23+8+EQLMeDJWcEVrSA2pOBs1KB5Uh2ww8Q+9fYDljS67k3GzU4DIDBa6+qRIgfZ2Bp+gQ==", "dev": true, "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" + "define-data-property": "^1.1.0", + "functions-have-names": "^1.2.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } + "node_modules/mock-property/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true }, - "node_modules/source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", - "deprecated": "See https://github.com/lydell/source-map-url#deprecated", + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/object-inspect": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.0.tgz", + "integrity": "sha512-HQ4J+ic8hKrgIt3mqk6cVOVrW2ozL4KdvHlqpBv9vDYWx9ysAgENAdvy4FoGF+KFdhR7nQTNm5J0ctAeOwn+3g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", "dev": true, "dependencies": { - "extend-shallow": "^3.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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 + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "dev": true, "dependencies": { - "escape-string-regexp": "^2.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "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, - "engines": { - "node": ">=8" + "dependencies": { + "wrappy": "1" } }, - "node_modules/static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "dependencies": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8.0" } }, - "node_modules/static-extend/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { - "is-descriptor": "^0.1.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/static-extend/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "dependencies": { - "kind-of": "^3.0.2" + "callsites": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/static-extend/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, "engines": { "node": ">=0.10.0" } }, - "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "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, - "dependencies": { - "is-buffer": "^1.1.5" - }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/static-extend/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "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 + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/static-extend/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "node_modules/playwright": { + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz", + "integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==", "dev": true, "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" + "playwright-core": "1.39.0" + }, + "bin": { + "playwright": "cli.js" }, "engines": { - "node": ">=10" + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" } }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/playwright-core": { + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz", + "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==", "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "bin": { + "playwright-core": "cli.js" }, "engines": { - "node": ">=8" + "node": ">=16" } }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "engines": { - "node": ">=8" + "node": ">= 0.8.0" } }, - "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==", + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "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==", + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true, "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==", + "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" + } + ] + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "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, - "dependencies": { - "has-flag": "^3.0.0" - }, "engines": { - "node": ">=4" + "iojs": ">=1.0.0", + "node": ">=0.10.0" } }, - "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==", + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, - "engines": { - "node": ">= 0.4" + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true - }, - "node_modules/sync-fetch": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/sync-fetch/-/sync-fetch-0.5.2.tgz", - "integrity": "sha512-6gBqqkHrYvkH65WI2bzrDwrIKmt3U10s4Exnz3dYuE5Ah62FIfNv/F63inrNhu2Nyh3GH5f42GKU3RrSJoaUyQ==", - "dependencies": { - "node-fetch": "^2.6.1" + "node_modules/rollup": { + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "dev": true, + "peer": true, + "bin": { + "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=14" + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "node_modules/rollup-plugin-polyfill-node": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-polyfill-node/-/rollup-plugin-polyfill-node-0.12.0.tgz", + "integrity": "sha512-PWEVfDxLEKt8JX1nZ0NkUAgXpkZMTb85rO/Ru9AQ69wYW8VUCfDgP4CGRXXWYni5wDF0vIeR1UoF3Jmw/Lt3Ug==", "dev": true, "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" + "@rollup/plugin-inject": "^5.0.1" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0 || ^3.0.0" } }, - "node_modules/test-exclude/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==", + "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" + } + ], "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "queue-microtask": "^1.2.2" } }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/safe-array-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", + "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" }, "engines": { - "node": "*" + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true }, - "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, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", "dev": true, "dependencies": { - "kind-of": "^3.0.2" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/to-object-path/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", "dev": true, "dependencies": { - "is-buffer": "^1.1.5" + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "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, "dependencies": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "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==", + "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, - "dependencies": { - "is-number": "^7.0.0" - }, "engines": { - "node": ">=8.0" + "node": ">=8" } }, - "node_modules/tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "node_modules/shiki": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.3.tgz", + "integrity": "sha512-U3S/a+b0KS+UkTyMjoNojvTgrBHjgp7L6ovhFVZsXmBGnVdQ4K4U9oK0z63w538S91ATngv1vXigHCSWOwnr+g==", "dev": true, "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" } }, - "node_modules/tr46": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", - "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", "dev": true, "dependencies": { - "punycode": "^2.1.1" + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" }, - "engines": { - "node": ">=12" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ts-jest": { - "version": "29.1.1", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", - "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, - "dependencies": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "^7.5.3", - "yargs-parser": "^21.0.1" - }, - "bin": { - "ts-jest": "cli.js" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } + "node": ">=8" } }, - "node_modules/ts-jest/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==", + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", "dev": true, "dependencies": { - "yallist": "^4.0.0" + "internal-slot": "^1.0.4" }, "engines": { - "node": ">=10" + "node": ">= 0.4" } }, - "node_modules/ts-jest/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==", + "node_modules/string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-jest/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 - }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tslint": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", - "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", - "deprecated": "TSLint has been deprecated in favor of ESLint. Please see https://github.com/palantir/tslint/issues/4534 for more information.", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^4.0.1", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.3", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.13.0", - "tsutils": "^2.29.0" - }, - "bin": { - "tslint": "bin/tslint" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { - "node": ">=4.8.0" - }, - "peerDependencies": { - "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev" - } - }, - "node_modules/tslint-config-prettier": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz", - "integrity": "sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg==", - "dev": true, - "bin": { - "tslint-config-prettier-check": "bin/check.js" + "node": ">= 0.4" }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/tslint/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, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/string.prototype.trimend": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, - "engines": { - "node": "*" - } - }, - "node_modules/tslint/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "node_modules/string.prototype.trimstart": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", "dev": true, "dependencies": { - "tslib": "^1.8.1" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, - "peerDependencies": { - "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "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, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "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, - "dependencies": { - "is-typedarray": "^1.0.0" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/typedoc": { - "version": "0.23.28", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.28.tgz", - "integrity": "sha512-9x1+hZWTHEQcGoP7qFmlo4unUoVJLB0H/8vfO/7wqTnZxg4kPuji9y3uRzEu0ZKez63OJAUmiGhUrtukC6Uj3w==", + "node_modules/tape": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/tape/-/tape-5.7.1.tgz", + "integrity": "sha512-5U2nU0PbvWXXtJxE0hFv78VSyhJiyEPq1wNtMCWUxt17SZbeBqs9V5zcyDqkyguYN39d8qAjZxfFkjJ1+kB4yA==", "dev": true, "dependencies": { - "lunr": "^2.3.9", - "marked": "^4.2.12", - "minimatch": "^7.1.3", - "shiki": "^0.14.1" + "@ljharb/resumer": "^0.0.1", + "@ljharb/through": "^2.3.11", + "array.prototype.every": "^1.1.5", + "call-bind": "^1.0.2", + "deep-equal": "^2.2.2", + "defined": "^1.0.1", + "dotignore": "^0.1.2", + "for-each": "^0.3.3", + "get-package-type": "^0.1.0", + "glob": "^7.2.3", + "has": "^1.0.3", + "has-dynamic-import": "^2.0.1", + "inherits": "^2.0.4", + "is-regex": "^1.1.4", + "minimist": "^1.2.8", + "mock-property": "^1.0.0", + "object-inspect": "^1.12.3", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "resolve": "^2.0.0-next.5", + "string.prototype.trim": "^1.2.8" }, "bin": { - "typedoc": "bin/typedoc" - }, - "engines": { - "node": ">= 14.14" + "tape": "bin/tape" }, - "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/typedoc/node_modules/minimatch": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", - "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", + "node_modules/tape/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" }, - "engines": { - "node": ">=10" + "bin": { + "resolve": "bin/resolve" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true }, - "node_modules/union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "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, "dependencies": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" + "is-number": "^7.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8.0" } }, - "node_modules/union-value/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" } }, - "node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, "engines": { - "node": ">= 4.0.0" + "node": ">= 0.8.0" } }, - "node_modules/unset-value": { + "node_modules/typed-array-buffer": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", "dev": true, "dependencies": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/unset-value/node_modules/has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", "dev": true, "dependencies": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", "dev": true, "dependencies": { - "isarray": "1.0.0" + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/unset-value/node_modules/has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "node_modules/typedoc": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.2.tgz", + "integrity": "sha512-286F7BeATBiWe/qC4PCOCKlSTwfnsLbC/4cZ68oGBbvAqb9vV33quEOXx7q176OXotD+JdEerdQ1OZGJ818lnA==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "lunr": "^2.3.9", + "marked": "^4.3.0", + "minimatch": "^9.0.3", + "shiki": "^0.14.1" }, "bin": { - "update-browserslist-db": "cli.js" + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 16" }, "peerDependencies": { - "browserslist": ">= 4.21.0" + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x" } }, - "node_modules/urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", - "deprecated": "Please see https://github.com/lydell/urix#deprecated", - "dev": true - }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" } }, - "node_modules/use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "node_modules/undici-types": { + "version": "5.25.3", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", + "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==", + "dev": true + }, + "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, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - }, - "engines": { - "node": ">=10.12.0" + "punycode": "^2.1.0" } }, "node_modules/vscode-oniguruma": { @@ -8268,74 +3588,11 @@ "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", "dev": true }, - "node_modules/w3c-xmlserializer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", - "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", - "dev": true, - "dependencies": { - "xml-name-validator": "^4.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "dev": true, - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-mimetype": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-url": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", - "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", - "dev": true, - "dependencies": { - "tr46": "^3.0.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "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, "dependencies": { "isexe": "^2.0.0" }, @@ -8346,153 +3603,62 @@ "node": ">= 8" } }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/which-typed-array": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=7.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "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 }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml-name-validator": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", - "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 16946f3..28a6efb 100644 --- a/package.json +++ b/package.json @@ -3,11 +3,11 @@ "version": "1.0.0-rc1", "description": "Extism runtime for JavaScript", "scripts": { - "build": "node build.js && tsc --emitDeclarationOnly --project ./tsconfig.node.json --declaration --outDir dist && tsc --emitDeclarationOnly --project ./tsconfig.browser.json --declaration --outDir dist", - "format": "prettier --write \"src/**/*.ts\"", - "lint": "tslint -p tsconfig.json", - "test": "cross-env NODE_OPTIONS='--experimental-wasi-unstable-preview1' jest --coverage --config jest.config.js", - "test-deno": "deno test -A ./tests/mod.test.ts" + "build": "just build", + "format": "just format", + "test": "just test", + "serve": "just serve", + "lint": "just lint" }, "private": false, "publishConfig": { @@ -16,43 +16,42 @@ "files": [ "dist/*" ], - "module": "dist/browser/index.mjs", - "main": "dist/node/index.js", - "typings": "dist/node/index.d.ts", - "browser": "./dist/browser/index.mjs", + "module": "dist/esm/mod.js", + "main": "dist/cjs/index.js", + "typings": "./dist/cjs/mod.d.ts", + "browser": "./dist/browser/mod.js", "exports": { - ".": { + "node": { "import": { - "browser": "./dist/browser/index.mjs", - "types": "./dist/browser/index.d.ts" + "types": "./dist/esm/mod.d.ts", + "default": "./dist/esm/mod.js" }, "require": { - "node": "./dist/node/index.js", - "types": "./dist/node/index.d.ts" + "types": "./dist/cjs/mod.d.ts", + "default": "./dist/cjs/index.js" } + }, + "default": { + "types": "./dist/browser/mod.d.ts", + "default": "./dist/browser/mod.js" } }, "author": "The Extism Authors ", "license": "BSD-3-Clause", "devDependencies": { - "@types/jest": "^29.2.2", - "@types/sync-fetch": "^0.4.0", + "@bjorn3/browser_wasi_shim": "^0.2.14", + "@playwright/test": "^1.39.0", + "@types/node": "^20.8.7", + "@typescript-eslint/eslint-plugin": "^6.8.0", + "@typescript-eslint/parser": "^6.8.0", "esbuild": "^0.15.13", - "esbuild-jest": "^0.5.0", - "jest": "^29.6.4", - "jest-environment-jsdom": "^29.3.1", + "esbuild-node-builtin": "^0.1.1", + "eslint": "^8.51.0", + "just-install": "^1.0.11", + "playwright": "^1.39.0", "prettier": "^2.7.1", - "ts-jest": "^29.0.3", - "tslint": "^6.1.3", - "tslint-config-prettier": "^1.18.0", - "typedoc": "^0.23.20", - "typescript": "^4.8.4" - }, - "dependencies": { - "@bjorn3/browser_wasi_shim": "^0.2.14", - "cross-env": "^7.0.3", - "crypto": "^1.0.1", - "minimatch": "^9.0.3", - "sync-fetch": "^0.5.2" + "tape": "^5.7.1", + "typedoc": "^0.25.2", + "typescript": "^5.2.2" } } diff --git a/playwright.config.js b/playwright.config.js new file mode 100644 index 0000000..499a0be --- /dev/null +++ b/playwright.config.js @@ -0,0 +1,14 @@ +import { defineConfig } from '@playwright/test'; + +const config = defineConfig({ + webServer: { + command: 'echo', + url: 'http://127.0.0.1:8124/dist/tests/browser/', + reuseExistingServer: true, + stdout: 'ignore', + stderr: 'pipe', + }, +}) + +config.testDir = 'tests/'; +export default config; diff --git a/src/background-plugin.ts b/src/background-plugin.ts new file mode 100644 index 0000000..6ac7fcb --- /dev/null +++ b/src/background-plugin.ts @@ -0,0 +1,419 @@ +import { CallContext, IMPORT_STATE, EXPORT_STATE, STORE, GET_BLOCK } from './call-context.ts'; +import { type InternalConfig } from './mod.ts'; +import { WORKER_URL } from 'js-sdk:worker-url'; +import { Worker } from 'node:worker_threads'; +import { FEATURES } from 'js-sdk:features'; + +const MAX_WAIT = 5000 + +enum SharedArrayBufferSection { + End = 0, + RetI64 = 1, + RetF64 = 2, + RetVoid = 3, + Block = 4, +} + +// Firefox has not yet implemented Atomics.waitAsync, but we can polyfill +// it using a worker as a one-off. +// +// TODO: we should probably give _each_ background plugin its own waiter +// script. +const AtomicsWaitAsync = Atomics.waitAsync || (() => { + const src = `onmessage = ev => { + const [b, i, v] = ev.data + const f = new Int32Array(b) + postMessage(Atomics.wait(f, i, v)); + }` + + const blob = new (Blob as any)([src], { type: 'text/javascript' }) + const url = URL.createObjectURL(blob) + const w = new Worker(url); + return (ia: any, index, value) => { + const promise = new Promise(resolve => { + w.once('message', data => { + resolve(data) + }) + }) + w.postMessage([ia.buffer, index, value]) + return promise + } +})() + +class BackgroundPlugin { + worker: Worker + sharedData: SharedArrayBuffer + sharedDataView: DataView + hostFlag: Int32Array + opts: InternalConfig + + #context: CallContext; + #request: [(result: any[]) => void, (result: any[]) => void] | null = null; + + constructor( + worker: Worker, + sharedData: SharedArrayBuffer, + opts: InternalConfig, + context: CallContext, + ) { + this.worker = worker; + this.sharedData = sharedData; + this.sharedDataView = new DataView(sharedData); + this.hostFlag = new Int32Array(sharedData); + this.opts = opts; + this.#context = context; + + this.worker.on('message', ev => this.#handleMessage(ev)) + } + + async #handleMessage(ev: any) { + switch (ev?.type) { + case 'invoke': return this.#handleInvoke(ev); + case 'return': return this.#handleReturn(ev); + case 'log': return this.#handleLog(ev); + } + } + + #handleLog(ev: any) { + const fn = (this.opts.logger as any)[ev.level as string] + if (typeof fn !== 'function') { + this.opts.logger?.error(`failed to find loglevel="${ev.level}" on logger: message=${ev.message}`) + } else { + fn.call(this.opts.logger, ev.message) + } + } + + #handleReturn(ev: any) { + const responder = this.#request || null; + if (responder === null) { + // This is fatal, we should probably panic + throw new Error(`received "return" call with no corresponding request`) + } + + this.#request = null; + + const [resolve, reject] = responder + + if (!Array.isArray(ev.results) || ev.results.length !== 2) { + return reject(new Error(`received malformed "return"`) as any) + } + + const [err, data] = ev.results + + err ? reject(err) : resolve(data) + } + + // host -> guest() invoke + async #invoke(handler: string, ...args: any[]): Promise { + if (this.#request) { + throw new Error('plugin is not reentrant') + } + let resolve, reject; + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + + this.#request = [resolve as any, reject as any]; + + this.worker.postMessage({ + type: 'invoke', + handler, + args + }) + + return promise; + } + + async functionExists(funcName: string | [string, string]): Promise { + return await this.#invoke('functionExists', funcName) + } + + // host -> guest invoke() + async call(funcName: string | [string, string], input?: string | Uint8Array): Promise { + const index = this.#context[STORE](input); + + const [errorIdx, outputIdx] = await this.callBlock(funcName, index); + + const shouldThrow = errorIdx !== null + const idx = errorIdx ?? outputIdx + + if (idx === null) { + return null + } + + const block = this.#context[GET_BLOCK](idx); + + if (block === null) { + return null + } + + let buf; + if (FEATURES.allowSharedBufferCodec) { + buf = new Uint8Array(block.buffer) + } else { + // This platform doesn't support encoding/decoding SharedArrayBuffers. + // Copy into a non-sharedarraybuffer so that the output works with TextDecoder. + buf = new Uint8Array(block.buffer.byteLength); + buf.set(new Uint8Array(block.buffer)); + } + + if (shouldThrow) { + const msg = new TextDecoder().decode(buf) + throw new Error(`Plugin-originated error: ${msg}`) + } + + return buf; + } + + async callBlock(funcName: string | [string, string], input: number | null): Promise<[number | null, number | null]> { + const exported = this.#context[EXPORT_STATE](); + const { results, state } = await this.#invoke('call', funcName, input, exported); + this.#context[IMPORT_STATE](state, true); + + const [err, data] = results + if (err) { + throw err + } + + return data + } + + // guest -> host invoke() + async #handleInvoke(ev: any) { + const namespace = this.opts.functions[ev.namespace]; + const func = (namespace ?? {})[ev.func]; + try { + if (!func) { + throw Error(`Plugin error: host function "${ev.namespace}" "${ev.func}" does not exist`); + } + + this.#context[IMPORT_STATE](ev.state, true) + + const data = await func(this.#context, ...ev.args) + + const { blocks } = this.#context[EXPORT_STATE]() + + // Writes to the ring buffer MAY return a promise if the write would wrap. + // Writes that fit within the ring buffer return void. + const writer = new RingBufferWriter(this.sharedData) + let promise: any; + for (const [buffer, destination] of blocks) { + promise = writer.writeUint8(SharedArrayBufferSection.Block) + if (promise) { + await promise + } + + promise = writer.writeUint32(destination) + if (promise) { + await promise + } + + promise = writer.writeUint32(buffer?.byteLength || 0) + if (promise) { + await promise + } + + if (buffer) { + promise = writer.write(new Uint8Array(buffer)) + if (promise) { + await promise + } + } + } + + if (typeof data === 'bigint') { + promise = writer.writeUint8(SharedArrayBufferSection.RetI64) + if (promise) { + await promise + } + + promise = writer.writeUint64(data) + if (promise) { + await promise + } + } else if(typeof data === 'number') { + promise = writer.writeUint8(SharedArrayBufferSection.RetF64) + if (promise) { + await promise + } + + promise = writer.writeFloat64(data) + if (promise) { + await promise + } + } else { + promise = writer.writeUint8(SharedArrayBufferSection.RetVoid) + if (promise) { + await promise + } + } + await writer.writeUint8(SharedArrayBufferSection.End) as any || writer.flush() + } catch (err) { + this.close(); + const [, reject] = this.#request as any[] + this.#request = null + return reject(err) + } + } + + async getExports(name?: string): Promise { + return await this.#invoke('getExports', name ?? '0') + } + + async getImports(name?: string): Promise { + return await this.#invoke('getImports', name ?? '0') + } + + async getInstance(): Promise { + throw new Error('todo'); + } + + async close(): Promise { + if (this.worker) { + this.worker.terminate(); + this.worker = null as any; + } + } +} + +export async function createBackgroundPlugin( + opts: InternalConfig, + names: string[], + modules: ArrayBuffer[], +): Promise { + const worker = new Worker(WORKER_URL); + const context = new CallContext(SharedArrayBuffer, opts.logger, opts.config); + + await new Promise((resolve, reject) => { + worker.on('message', function handler(ev) { + if (ev?.type !== 'initialized') { + reject(new Error(`received unexpected message (type=${ev?.type})`)) + } + + worker.removeListener('message', handler); + resolve(null); + }) + }); + + // NB(chrisdickinson): We *have* to create the SharedArrayBuffer in + // the parent context because -- for whatever reason! -- chromium does + // not allow the creation of shared buffers in worker contexts, but firefox + // and webkit do. + const sharedData = new (SharedArrayBuffer as any)(1 << 16); + + const { fetch: _, logger: __, ...rest } = opts; + const message = { + ...rest, + type: 'init', + functions: Object.fromEntries( + Object.entries(opts.functions || {}).map(([k, v]) => [k, Object.keys(v)]) + ), + names, + modules, + sharedData, + }; + + const onready = new Promise((resolve, reject) => { + worker.on('message', function handler(ev) { + if (ev?.type !== 'ready') { + reject(new Error(`received unexpected message (type=${ev?.type})`)) + } + + worker.removeListener('message', handler); + resolve(null); + }) + }); + + worker.postMessage(message, modules); + await onready + + return new BackgroundPlugin( + worker, + sharedData, + opts, + context + ); +} + +class RingBufferWriter { + output: SharedArrayBuffer + scratch: ArrayBuffer + scratchView: DataView + outputOffset: number + + constructor(output: SharedArrayBuffer) { + this.scratch = new ArrayBuffer(8) + this.scratchView = new DataView(this.scratch) + this.output = output; + this.outputOffset = 4; + } + + async spanningWrite(input: Uint8Array) { + let outputOffset = this.outputOffset + let inputOffset = 0 + let toWrite = this.output.byteLength - this.outputOffset + let flushedWriteCount = 1 + Math.floor((input.byteLength - toWrite) / this.output.byteLength) + const finalWrite = (input.byteLength - toWrite) % this.output.byteLength; + do { + new Uint8Array(this.output).set(input.subarray(inputOffset, toWrite), outputOffset) + await this.flush() + inputOffset += toWrite + outputOffset = 4 + toWrite = this.output.byteLength - 4 + --flushedWriteCount + } while(flushedWriteCount != 0) + + if (finalWrite) { + new Uint8Array(this.output).set(input.subarray(inputOffset, finalWrite), outputOffset) + outputOffset += finalWrite + } + + this.outputOffset = outputOffset + } + + write(bytes: ArrayBufferLike): void | Promise { + if (bytes.byteLength + this.outputOffset < this.output.byteLength) { + new Uint8Array(this.output).set(new Uint8Array(bytes), this.outputOffset) + this.outputOffset += bytes.byteLength + return + } + + return this.spanningWrite(new Uint8Array(bytes)) + } + + writeUint8(value: number): void | Promise { + this.scratchView.setUint8(0, value) + return this.write(this.scratch.slice(0, 1)) + } + + writeUint32(value: number): void | Promise { + this.scratchView.setUint32(0, value, true) + return this.write(this.scratch.slice(0, 4)) + } + + writeUint64(value: bigint): void | Promise { + this.scratchView.setBigUint64(0, value, true) + return this.write(this.scratch.slice(0, 8)) + } + + writeFloat64(value: number): void | Promise { + this.scratchView.setFloat64(0, value, true) + return this.write(this.scratch.slice(0, 8)) + } + + async flush() { + const flag = new Int32Array(this.output) + + Atomics.store(flag, 0, this.outputOffset) + Atomics.notify(flag, 0) + const result = AtomicsWaitAsync(flag, 0, this.outputOffset, MAX_WAIT) + if (result.async) { + result.value = await result.value as any + } + + if (result.value === 'timed-out') { + throw new Error('encountered timeout while flushing host function to worker memory') + } + } +} diff --git a/src/browser/index.ts b/src/browser/index.ts deleted file mode 100644 index 84773a8..0000000 --- a/src/browser/index.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { - ExtismPluginBase, - PluginWasi, - ExtismPluginOptions, - fetchModuleData, - instantiateExtismRuntime, - Manifest, - ManifestWasm, - ManifestWasmData, - ManifestWasmUrl, - HttpRequest, - HttpResponse, - embeddedRuntime, - embeddedRuntimeHash, - CurrentPlugin, - StreamingSource, - isURL, -} from '../plugin'; - -import { WASI, Fd, File, OpenFile } from '@bjorn3/browser_wasi_shim'; -import { minimatch } from 'minimatch'; - -class ExtismPlugin extends ExtismPluginBase { - protected supportsHttpRequests(): boolean { - return true; - } - - protected httpRequest(request: HttpRequest, body: Uint8Array | null): HttpResponse { - if (request.method == 'GET' || request.method == 'HEAD') { - body = null; - } - - const xhr = new XMLHttpRequest(); - - // Open the request synchronously - xhr.open(request.method, request.url, false); - - // Set headers - for (const key in request.headers) { - xhr.setRequestHeader(key, request.headers[key]); - } - - xhr.send(body); - - let responseBody: Uint8Array; - - switch (xhr.responseType) { - case 'arraybuffer': - responseBody = new Uint8Array(xhr.response); - break; - case 'blob': - throw new Error('Blob response type is not supported in a synchronous context.'); - case 'document': - case 'json': - case 'text': - case '': - const encoder = new TextEncoder(); - responseBody = encoder.encode(String(xhr.response)); - break; - default: - throw new Error(`Unknown response type: ${xhr.responseType}`); - } - - return { - body: responseBody, - status: xhr.status, - }; - } - - protected matches(text: string, pattern: string): boolean { - return minimatch(text, pattern); - } - - protected loadWasi(options: ExtismPluginOptions): PluginWasi { - const args: Array = []; - const envVars: Array = []; - let fds: Fd[] = [ - new OpenFile(new File([])), // stdin - new OpenFile(new File([])), // stdout - new OpenFile(new File([])), // stderr - ]; - - const wasi = new WASI(args, envVars, fds); - - return new PluginWasi(wasi, wasi.wasiImport, instance => this.initialize(wasi, instance)); - } - - private initialize(wasi: WASI, instance: WebAssembly.Instance) { - const wrapper = { - exports: { - memory: instance.exports.memory as WebAssembly.Memory, - _start() { }, - }, - }; - - if (!wrapper.exports.memory) { - throw new Error('The module has to export a default memory.'); - } - - wasi.start(wrapper); - } -} - -/** - * Create a new plugin. - * @param manifestData An Extism manifest {@link Manifest} or a Wasm module. - * @param options Options for initializing the plugin. - * @returns {ExtismPlugin} An initialized plugin. - */ -async function createPlugin( - manifestData: Manifest | ManifestWasm | ArrayBuffer | string | URL, - options: ExtismPluginOptions, -): Promise { - - if (typeof manifestData === "string" || manifestData instanceof URL) { - manifestData = { - url: manifestData as string | URL - } - } - - let moduleData = await fetchModuleData(manifestData, fetchWasm, calculateHash); - - const runtimeWasm = options.runtime ?? { - data: toBytes(embeddedRuntime), - hash: embeddedRuntimeHash - }; - - let runtime = await instantiateExtismRuntime(runtimeWasm, fetchWasm, calculateHash); - - return new ExtismPlugin(runtime, moduleData, options); - - function toBytes(base64: string): Uint8Array { - const binaryString = window.atob(base64); - const len = binaryString.length; - const bytes = new Uint8Array(len); - - for (let i = 0; i < len; i++) { - bytes[i] = binaryString.charCodeAt(i); - } - - return bytes; - } - - async function calculateHash(data: ArrayBuffer) { - const hashBuffer = await crypto.subtle.digest('SHA-256', data); - const hashArray = Array.from(new Uint8Array(hashBuffer)); - const hashHex = hashArray.map((byte) => byte.toString(16).padStart(2, '0')).join(''); - return hashHex; - } - - async function fetchWasm(wasm: ManifestWasm): Promise { - let data: ArrayBuffer; - - if ((wasm as ManifestWasmData).data) { - data = (wasm as ManifestWasmData).data; - } else if ((wasm as ManifestWasmUrl).url) { - // In the browser, it's useful to support relative paths, - // so we assume the URL we get doesn't point to local files - const url = (wasm as ManifestWasmUrl).url; - return await fetch(url); - } else { - throw new Error(`Unrecognized wasm source: ${wasm}`); - } - - return data; - } -} - -if (window) { - // @ts-ignore - window.createPlugin = createPlugin; -} - -export default createPlugin; -export type { ExtismPlugin, CurrentPlugin, ExtismPluginOptions, Manifest, ManifestWasm, ManifestWasmData, ManifestWasmUrl }; diff --git a/src/call-context.ts b/src/call-context.ts new file mode 100644 index 0000000..150b51f --- /dev/null +++ b/src/call-context.ts @@ -0,0 +1,348 @@ +import { type PluginConfig } from './mod.ts'; +import { FEATURES } from 'js-sdk:features'; + +export const BEGIN = Symbol('begin') +export const END = Symbol('end') +export const ENV = Symbol('env') +export const GET_BLOCK = Symbol('get-block') +export const IMPORT_STATE = Symbol('import-state') +export const EXPORT_STATE = Symbol('export-state') +export const STORE = Symbol('store-value') + +export class Block { + buffer: ArrayBufferLike + view: DataView + local: boolean + + get byteLength() { + return this.buffer.byteLength + } + + constructor(arrayBuffer: ArrayBufferLike, local: boolean) { + this.buffer = arrayBuffer + this.view = new DataView(this.buffer) + this.local = local + } + + static indexToAddress(idx: bigint | number): bigint { + return BigInt(idx) << 48n; + } + + static addressToIndex(addr: bigint | number): number { + return Number(BigInt(addr) >> 48n); + } + + static maskAddress(addr: bigint | number): number { + return Number(BigInt(addr) & ((1n << 48n) - 1n)); + } +} + +export type CallState = { blocks: [ArrayBufferLike | null, number][], stack: [number | null, number | null, number | null][] }; + +export class CallContext { + #stack: [number | null, number | null, number | null][]; + #blocks: (Block | null)[] = []; + #logger: Console + #decoder: TextDecoder; + #encoder: TextEncoder; + #arrayBufferType: { new(size: number): ArrayBufferLike ;} + #config: PluginConfig; + + readString(addr: bigint | number): string | null { + const blockIdx = Block.addressToIndex(addr); + const block = this.#blocks[blockIdx]; + if (!block) { + return null + } + + const buffer = ( + (!(block.buffer instanceof ArrayBuffer) && !FEATURES.allowSharedBufferCodec) + ? new Uint8Array(block.buffer).slice().buffer + : block.buffer + ); + + return this.#decoder.decode(buffer); + } + + store(input: string | Uint8Array | number): bigint { + const idx = this[STORE](input); + if (!idx) { + throw new Error('failed to store output') + } + return Block.indexToAddress(idx) + } + + [ENV] = { + extism_alloc: (n: bigint): bigint => { + const block = this.alloc(n) + const addr = Block.indexToAddress(block) + return addr; + }, + + extism_free: (addr: number) => { + this.#blocks[Block.addressToIndex(addr)] = null; + }, + + extism_load_u8: (addr: bigint): number => { + const blockIdx = Block.addressToIndex(addr); + const offset = Block.maskAddress(addr); + const block = this.#blocks[blockIdx]; + return block?.view.getUint8(Number(offset)) as number; + }, + + extism_load_u64: (addr: bigint): bigint => { + const blockIdx = Block.addressToIndex(addr); + const offset = Block.maskAddress(addr); + const block = this.#blocks[blockIdx]; + return block?.view.getBigUint64(Number(offset), true) as bigint; + }, + + extism_store_u8: (addr: bigint, n: number) => { + const blockIdx = Block.addressToIndex(addr); + const offset = Block.maskAddress(addr); + const block = this.#blocks[blockIdx]; + block?.view.setUint8(Number(offset), Number(n)); + }, + + extism_store_u64: (addr: bigint, n: bigint) => { + const blockIdx = Block.addressToIndex(addr); + const offset = Block.maskAddress(addr); + const block = this.#blocks[blockIdx]; + block?.view.setBigUint64(Number(offset), n, true); + }, + + extism_input_length: () => { + return BigInt(this.#input?.byteLength ?? 0); + }, + + extism_input_load_u8: (addr: bigint): number => { + const offset = Block.maskAddress(addr); + return this.#input?.view.getUint8(Number(offset)) as number; + }, + + extism_input_load_u64: (addr: bigint): bigint => { + const offset = Block.maskAddress(addr); + return this.#input?.view.getBigUint64(Number(offset), true) as bigint; + }, + + extism_output_set: (addr: bigint, length: bigint) => { + const blockIdx = Block.addressToIndex(addr); + const block = this.#blocks[blockIdx] + if (!block) { + throw new Error('cannot assign to this block') + } + + if (length > block.buffer.byteLength) { + throw new Error('length longer than target block') + } + + this.#stack[this.#stack.length - 1][1] = blockIdx; + }, + + extism_error_set: (addr: bigint) => { + const blockIdx = Block.addressToIndex(addr); + const block = this.#blocks[blockIdx] + if (!block) { + throw new Error('cannot assign to this block') + } + + this.#stack[this.#stack.length - 1][2] = blockIdx; + }, + + extism_config_get: (addr: bigint): bigint => { + const key = this.readString(addr); + + if (key === null) { + return 0n + } + + if (key in this.#config) { + return this.store(this.#config[key]) + } + + return 0n + }, + + extism_var_get(_i: bigint): bigint { + return 0n + }, + + extism_var_set(_n: bigint, _i: bigint) { + }, + + extism_http_request(_requestOffset: bigint, _bodyOffset: bigint): bigint { + return 0n + }, + + extism_http_status_code(): number { + return 0 + }, + + extism_length: (addr: bigint): bigint => { + const blockIdx = Block.addressToIndex(addr); + const block = this.#blocks[blockIdx]; + if (!block) { + return 0n; + } + return BigInt(block.buffer.byteLength) + }, + + extism_log_warn: (addr: bigint) => { + const blockIdx = Block.addressToIndex(addr); + const block = this.#blocks[blockIdx]; + if (!block) { + return this.#logger.error(`failed to log(warn): bad block reference in addr 0x${addr.toString(16).padStart(64, '0')}`) + } + const text = this.#decoder.decode(block.buffer); + this.#logger.warn(text); + }, + + extism_log_info: (addr: bigint) => { + const blockIdx = Block.addressToIndex(addr); + const block = this.#blocks[blockIdx]; + if (!block) { + return this.#logger.error(`failed to log(info): bad block reference in addr 0x${addr.toString(16).padStart(64, '0')}`) + } + const text = this.#decoder.decode(block.buffer); + this.#logger.info(text); + }, + + extism_log_debug: (addr: bigint) => { + const blockIdx = Block.addressToIndex(addr); + const block = this.#blocks[blockIdx]; + if (!block) { + return this.#logger.error(`failed to log(debug): bad block reference in addr 0x${addr.toString(16).padStart(64, '0')}`) + } + const text = this.#decoder.decode(block.buffer); + this.#logger.debug(text); + }, + + extism_log_error: (addr: bigint) => { + const blockIdx = Block.addressToIndex(addr); + const block = this.#blocks[blockIdx]; + if (!block) { + return this.#logger.error(`failed to log(error): bad block reference in addr 0x${addr.toString(16).padStart(64, '0')}`) + } + const text = this.#decoder.decode(block.buffer); + this.#logger.error(text); + }, + } + + get #input(): Block | null { + const idx = this.#stack[this.#stack.length - 1][0] + if (idx === null) { + return null + } + return this.#blocks[idx] + } + + constructor(type: { new(size: number): ArrayBufferLike ;}, logger: Console, config: PluginConfig) { + this.#arrayBufferType = type + this.#logger = logger + this.#decoder = new TextDecoder() + this.#encoder = new TextEncoder() + this.#stack = [] + + // reserve the null page. + this.alloc(1) + + this.#config = config + } + + alloc(size: bigint | number): number { + const block = new Block(new (this.#arrayBufferType)(Number(size)), true); + const index = this.#blocks.length; + this.#blocks.push(block); + return index; + } + + [GET_BLOCK](index: number): Block { + const block = this.#blocks[index]; + if (!block) { + throw new Error(`invalid block index: ${index}`) + } + return block + } + + [IMPORT_STATE](state: CallState, copy: boolean = false) { + // eslint-disable-next-line prefer-const + for (let [buf, idx] of state.blocks) { + if (buf && copy) { + const dst = new Uint8Array(new (this.#arrayBufferType)(Number(buf.byteLength))); + dst.set(new Uint8Array(buf)) + buf = dst.buffer + } + this.#blocks[idx] = buf ? new Block(buf, false) : null + } + this.#stack = state.stack + } + + [EXPORT_STATE](): CallState { + return { + stack: this.#stack.slice(), + blocks: this.#blocks.map((block, idx) => { + if (!block) { + return [null, idx] + } + + if (block.local) { + block.local = false + return [block.buffer, idx] + } + return null + }).filter(Boolean) as [ArrayBufferLike, number][] + } + } + + [STORE](input?: string | Uint8Array | number) { + if (!input) { + return null + } + + if (typeof input === 'string') { + input = this.#encoder.encode(input); + } + + if (input instanceof Uint8Array) { + if (input.buffer.constructor === this.#arrayBufferType) { + // no action necessary, wrap it up in a block + const idx = this.#blocks.length + this.#blocks.push(new Block(input.buffer, true)) + return idx + } + const idx = this.alloc(input.length); + const block = this.#blocks[idx] as Block; + const buf = new Uint8Array(block.buffer); + buf.set(input, 0); + return idx + } + + return input + } + + [BEGIN](input: number | null) { + this.#stack.push([input, null, null]) + } + + [END](): [number | null, number | null] { + const [, outputIdx, errorIdx] = this.#stack.pop() as (number | null)[]; + const outputPosition = errorIdx === null ? 1 : 0 + const idx = errorIdx ?? outputIdx + const result: [number | null, number | null] = [null, null] + + if (idx === null) { + return result + } + + const block = this.#blocks[idx] + + if (block === null) { + // TODO: this might be an error? we got an output idx but it referred to a freed (or non-existant) block + return result + } + + result[outputPosition] = idx + + return result + } +} diff --git a/src/deno/mod.ts b/src/deno/mod.ts deleted file mode 100644 index 4114dab..0000000 --- a/src/deno/mod.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { - ExtismPluginBase, - PluginWasi, - ExtismPluginOptions, - fetchModuleData, - instantiateExtismRuntime, - Manifest, - ManifestWasm, - ManifestWasmData, - ManifestWasmUrl, - HttpRequest, - HttpResponse, - embeddedRuntime, - embeddedRuntimeHash, - CurrentPlugin, - StreamingSource, - isURL, -} from '../plugin.ts'; -import Context from 'https://deno.land/std@0.200.0/wasi/snapshot_preview1.ts'; -import minimatch from 'https://deno.land/x/minimatch@v3.0.4/index.js'; -import { createHash } from 'https://deno.land/std@0.108.0/hash/mod.ts'; -import { decode } from 'https://deno.land/std@0.201.0/encoding/base64.ts'; -import * as path from 'https://deno.land/std@0.102.0/path/mod.ts'; - -class ExtismPlugin extends ExtismPluginBase { - protected supportsHttpRequests(): boolean { - return false; - } - - protected httpRequest(_: HttpRequest, __: Uint8Array | null): HttpResponse { - throw new Error('Method not implemented.'); - } - - protected matches(text: string, pattern: string): boolean { - return minimatch(text, pattern); - } - - protected loadWasi(options: ExtismPluginOptions): PluginWasi { - const context = new Context({ - preopens: options.allowedPaths, - }); - - return new PluginWasi(context, context.exports, (instance) => this.initialize(context, instance)); - } - - private initialize(context: Context, instance: WebAssembly.Instance) { - const memory = instance.exports.memory as WebAssembly.Memory; - - if (!memory) { - throw new Error('The module has to export a default memory.'); - } - - context.start({ - exports: { - memory, - _start: () => { }, - }, - }); - } -} - -/** - * Create a new plugin. - * @param manifestData An Extism manifest {@link Manifest} or a Wasm module. - * @param options Options for initializing the plugin. - * @returns {ExtismPlugin} An initialized plugin. - */ -async function createPlugin( - manifestData: Manifest | ManifestWasm | ArrayBuffer | string | URL, - options: ExtismPluginOptions, -): Promise { - - if (typeof manifestData === "string" || manifestData instanceof URL) { - manifestData = { - url: manifestData as string | URL - } - } - - const moduleData = await fetchModuleData(manifestData, fetchWasm, calculateHash); - - const runtimeWasm = options.runtime ?? { - data: decode(embeddedRuntime), - hash: embeddedRuntimeHash, - }; - - const runtime = await instantiateExtismRuntime(runtimeWasm, fetchWasm, calculateHash); - - return new ExtismPlugin(runtime, moduleData, options); - - async function fetchWasm(wasm: ManifestWasm): Promise { - let data: ArrayBuffer; - - if ((wasm as ManifestWasmData).data) { - data = (wasm as ManifestWasmData).data; - } else if ((wasm as ManifestWasmUrl).url) { - const url = (wasm as ManifestWasmUrl).url; - if (isURL(url)) { - return await fetch(url); - } else { - const absolutePath = path.resolve(url as string); - return await fetch(`file://${absolutePath}`); - } - } else { - throw new Error(`Unrecognized wasm source: ${wasm}`); - } - - return data; - } - - function calculateHash(data: ArrayBuffer): Promise { - return new Promise((resolve) => { - const hasher = createHash('sha256'); - hasher.update(data); - resolve(hasher.toString('hex')); - }); - } -} - -export default createPlugin; - -export type { ExtismPlugin, CurrentPlugin, ExtismPluginOptions, Manifest, ManifestWasm, ManifestWasmData, ManifestWasmUrl }; diff --git a/src/foreground-plugin.ts b/src/foreground-plugin.ts new file mode 100644 index 0000000..cebffa2 --- /dev/null +++ b/src/foreground-plugin.ts @@ -0,0 +1,176 @@ +import { CallContext, GET_BLOCK, BEGIN, END, ENV, STORE } from './call-context.ts'; +import { type InternalConfig } from './mod.ts'; +import { loadWasi } from 'js-sdk:wasi'; + +const DYLIBSO_ENV = 'env'; + +class ForegroundPlugin { + #context: CallContext + #modules: { guestType: string, module: WebAssembly.WebAssemblyInstantiatedSource}[] + #names: string[] + + constructor(context: CallContext, names: string[], modules: { guestType: string, module: WebAssembly.WebAssemblyInstantiatedSource}[]) { + this.#context = context; + this.#names = names; + this.#modules = modules; + } + + async functionExists(funcName: string | [string, string]): Promise { + try { + const search: string[] = [].concat(funcName); + const [target, name] = ( + search.length === 2 ? + [this.lookupTarget(search[0]), search[1]] : + [this.#modules.find(guest => { + const exports = WebAssembly.Module.exports(guest.module.module) + return exports.find(item => { + return item.name === search[0] && item.kind === 'function' + }) + }), search[0]] + ) + + if (!target) { + return false + } + + const func = target.module.instance.exports[name] as any; + + if (!func) { + return false + } + + return true + } catch { + // lookupTarget will throw if it cannot find the specified target; cast it into a boolean + return false + } + } + + async callBlock(funcName: string | [string, string], input: number | null): Promise<[number | null, number | null]> { + const search: string[] = [].concat(funcName); + const [target, name] = ( + search.length === 2 ? + [this.lookupTarget(search[0]), search[1]] : + [this.#modules.find(guest => { + const exports = WebAssembly.Module.exports(guest.module.module) + return exports.find(item => { + return item.name === search[0] && item.kind === 'function' + }) + }), search[0]] + ) + + if (!target) { + throw Error(`Plugin error: target "${search.join('" "')}" does not exist`); + } + const func = target.module.instance.exports[name] as any; + if (!func) { + throw Error(`Plugin error: function "${search.join('" "')}" does not exist`); + } + + this.#context[BEGIN](input ?? null) + try { + func(); + return this.#context[END]() + } catch(err) { + this.#context[END](); + throw err + } + } + + async call(funcName: string | [string, string], input?: string | Uint8Array): Promise { + const inputIdx = this.#context[STORE](input); + const [errorIdx, outputIdx] = await this.callBlock(funcName, inputIdx); + const shouldThrow = errorIdx !== null + const idx = errorIdx ?? outputIdx + + if (idx === null) { + return null + } + + const block = this.#context[GET_BLOCK](idx); + if (!block) { + return null + } + + const buf = new Uint8Array(block.buffer) + if (shouldThrow) { + const msg = new TextDecoder().decode(buf) + throw new Error(`Plugin-originated error: ${msg}`) + } + return buf + } + + private lookupTarget(name: any): {guestType: string, module: WebAssembly.WebAssemblyInstantiatedSource} { + const target = String(name ?? '0') + const idx = this.#names.findIndex(xs => xs === target); + if (idx === -1) { + throw new Error(`no module named "${name}"`); + } + return this.#modules[idx] + } + + async getExports(name?: string): Promise { + return WebAssembly.Module.exports(this.lookupTarget(name).module.module) || [] + } + + async getImports(name?: string): Promise { + return WebAssembly.Module.imports(this.lookupTarget(name).module.module) || [] + } + + async getInstance(name?: string): Promise { + return this.lookupTarget(name).module.instance + } + + async close(): Promise { + // noop + } +} + +export async function createForegroundPlugin( + opts: InternalConfig, + names: string[], + sources: ArrayBuffer[], + context: CallContext = new CallContext(ArrayBuffer, opts.logger, opts.config) +): Promise { + const wasi = opts.wasiEnabled ? await loadWasi(opts.allowedPaths) : null; + + const imports: Record> = { + ...(wasi ? {wasi_snapshot_preview1: await wasi.importObject()} : {}), + [DYLIBSO_ENV]: context[ENV], + } + + for (const namespace in opts.functions) { + imports[namespace] = imports[namespace] || {}; + for (const func in opts.functions[namespace]) { + imports[namespace][func] = opts.functions[namespace][func].bind(null, context); + } + } + + const modules = await Promise.all(sources.map(async (source, idx, all) => { + const isRuntime = idx === all.length; + const module = await WebAssembly.instantiate(source, (isRuntime ? {} : imports) as WebAssembly.Imports); + if (!isRuntime && module.instance.exports._start) { + await wasi?.initialize(module.instance); + } + + const guestType = ( + module.instance.exports._initialize ? 'reactor' : + module.instance.exports.hs_init ? 'haskell' : + module.instance.exports.__wasm_call_ctors ? 'command' : + 'none' + ); + + const init: any = ( + module.instance.exports._initialize ? module.instance.exports._initialize : + module.instance.exports.hs_init ? module.instance.exports.hs_init : + module.instance.exports.__wasm_call_ctors ? module.instance.exports.__wasm_call_ctors : + () => {} + ); + + init() + + return { module, guestType } + })); + + return new ForegroundPlugin(context, names, modules); +} diff --git a/src/manifest.ts b/src/manifest.ts new file mode 100644 index 0000000..90e3cae --- /dev/null +++ b/src/manifest.ts @@ -0,0 +1,174 @@ +import { PluginConfigLike } from './mod.ts'; +import { readFile } from 'js-sdk:fs'; + +/** + * Represents the raw bytes of a WASM file loaded into memory + */ +export interface ManifestWasmData { + data: Uint8Array; +} + +/** + * Represents a url to a WASM module + */ +export interface ManifestWasmUrl { + url: URL | string; +} + +/** + * Represents a path to a WASM module + */ +export interface ManifestWasmPath { + path: string; +} + +/** + * The WASM to load as bytes, a path, or a url + */ +export type ManifestWasm = ( + ManifestWasmUrl | + ManifestWasmData | + ManifestWasmPath +) & { + name?: string | undefined; + hash?: string | undefined; +}; + +/** + * The manifest which describes the {@link ExtismPlugin} code and + * runtime constraints. + * + * @see [Extism > Concepts > Manifest](https://extism.org/docs/concepts/manifest) + */ +export interface Manifest { + wasm: Array; + config?: PluginConfigLike | undefined; + allowed_hosts?: Array | undefined; +} + +/** + * Any type that can be converted into an Extism {@link Manifest}. + * - `object` instances that implement {@link Manifest} are validated. + * - `ArrayBuffer` instances are converted into {@link Manifest}s with a single {@link ManifestWasmData} member. + * - `URL` instances are fetched and their responses interpreted according to their `content-type` response header. `application/wasm` and `application/octet-stream` items + * are treated as {@link ManifestWasmData} items; `application/json` and `text/json` are treated as JSON-encoded {@link Manifest}s. + * - `string` instances that start with `http://`, `https://`, or `file://` are treated as URLs. + * - `string` instances that DO NOT start with `http://`, `https://`, or `file://` are treated as JSON-encoded {@link Manifest}s. + * + * @throws {@link TypeError} when `URL` parameters don't resolve to a known `content-type` + * @throws {@link TypeError} when the resulting {@link Manifest} does not contain a `wasm` member with valid {@link ManifestWasm} items. + */ +export type ManifestLike = Manifest | ArrayBuffer | string | URL; + +async function _populateWasmField(candidate: ManifestLike, _fetch: typeof fetch): Promise { + if (candidate instanceof ArrayBuffer) { + return { wasm: [{ data: new Uint8Array(candidate as ArrayBuffer) }] }; + } + + if (typeof candidate === 'string') { + if (candidate.search(/^\s*\{/g) === 0) { + return JSON.parse(candidate); + } + + if (candidate.search(/^(https?|file):\/\//) !== 0) { + return { wasm: [{ path: candidate }] } + } + + candidate = new URL(candidate); + } + + if (candidate instanceof URL) { + const response = await _fetch(candidate, { redirect: 'follow' }); + const contentType = response.headers.get('content-type') || 'application/octet-stream'; + + switch (contentType.split(';')[0]) { + case 'application/octet-stream': + case 'application/wasm': + return _populateWasmField(await response.arrayBuffer(), _fetch); + case 'application/json': + case 'text/json': + return _populateWasmField(JSON.parse(await response.text()), _fetch); + default: + throw new TypeError( + `While processing manifest URL "${candidate}"; expected content-type of "text/json", "application/json", "application/octet-stream", or "application/wasm"; got "${contentType}" after stripping off charset.` + ) + } + } + + if (!('wasm' in candidate)) { + throw new TypeError('Expected "wasm" key in manifest') + } + + if (!Array.isArray(candidate.wasm)) { + throw new TypeError('Expected "manifest.wasm" to be array') + } + + const badItemIdx = candidate.wasm.findIndex( + item => !('data' in item) && !('url' in item) && !('path' in item) + ); + if (badItemIdx > -1) { + throw new TypeError(`Expected every item in "manifest.wasm" to include either a "data", "url", or "path" key; got bad item at index ${badItemIdx}`) + } + + return {...candidate as Manifest}; +} + +export async function intoManifest(candidate: ManifestLike, _fetch: typeof fetch = fetch): Promise { + const manifest = await _populateWasmField(candidate, _fetch) as Manifest; + manifest.config ??= {}; + manifest.allowed_hosts = [].concat(manifest.allowed_hosts || [] as any); + return manifest; +} + +export async function toWasmModuleData(manifest: Manifest, _fetch: typeof fetch): Promise<[string[], ArrayBuffer[]]> { + const names: string[] = []; + + const manifestsWasm = await Promise.all(manifest.wasm.map(async (item, idx) => { + let buffer: ArrayBuffer; + if ((item as ManifestWasmData).data) { + const data = (item as ManifestWasmData).data; + + if ((data as Uint8Array).buffer) { + buffer = data.buffer; + } else { + buffer = data as ArrayBuffer; + } + } else if ((item as ManifestWasmPath).path) { + const path = (item as ManifestWasmPath).path; + const data = await readFile(path); + buffer = data.buffer as ArrayBuffer; + } else { + const response = await _fetch((item as ManifestWasmUrl).url, { + headers: { + accept: 'application/wasm;q=0.9,application/octet-stream;q=0.8', + 'user-agent': 'extism' + } + }) + + buffer = await response.arrayBuffer(); + } + + if (item.hash) { + const hashBuffer = new Uint8Array(await crypto.subtle.digest('SHA-256', buffer)); + const checkBuffer = new Uint8Array(32); + let eq = true; + for (let i = 0; i < 32; ++i) { + checkBuffer[i] = parseInt(item.hash.slice(i<<1, (i<<1) + 2), 16); + // do not exit early: we want to do a constant time comparison + eq = eq && (checkBuffer[i] === hashBuffer[i]); + } + const hashAsString = () => [...hashBuffer].map(xs => xs.toString(16).padStart(2, '0')).join(''); + + if (!eq) { + throw new Error(`Plugin error: hash mismatch. Expected: ${item.hash}. Actual: ${hashAsString()}`); + } + + item.name ??= hashAsString(); + } + + (names[idx]) = item.name ?? String(idx); + return buffer + })); + + return [names, manifestsWasm] +} diff --git a/src/mod.test.ts b/src/mod.test.ts new file mode 100644 index 0000000..2717527 --- /dev/null +++ b/src/mod.test.ts @@ -0,0 +1,321 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import createPlugin, { CallContext, FEATURES } from './mod.ts'; + +// The presence of `*.test.ts` files adjacent to module files is no mistake, sadly: +// we have to be in the same directory in order to preserve the `__dirname` / `import.meta.url` value +// between `mod.ts` and the tests in the build output. +test('createPlugin loads a module and provides lookups', async () => { + const plugin = await createPlugin('http://localhost:8124/wasm/code.wasm', { useWasi: true }); + + try { + assert(await plugin.functionExists('count_vowels'), 'count_vowels should exist'); + assert(await plugin.functionExists(['0', 'count_vowels']), '0:count_vowels should exist'); + assert(!await plugin.functionExists(['dne', 'count_vowels']), 'dne:count_vowels should not exist'); + assert(!await plugin.functionExists('count_sheep'), 'count_sheep should not exist'); + } finally { + await plugin.close(); + } +}); + +if (!FEATURES.crossOriginChecksEnforced) { + test('can create plugin from url with hash check', async () => { + const plugin = await createPlugin({wasm: [{ + url: "https://raw.githubusercontent.com/extism/extism/main/wasm/code.wasm", + hash: "7def5bb4aa3843a5daf5d6078f1e8540e5ef10b035a9d9387e9bd5156d2b2565" + }]}); + + try { + assert.equal(await plugin.functionExists('count_vowels'), true); + } finally { + await plugin.close() + } + }); +} + +test('createPlugin fails on hash mismatch (bad hash)', async () => { + const [err, plugin] = await createPlugin({ + wasm: [{ + url: 'http://localhost:8124/wasm/code.wasm', + hash: 'not a good hash' + }] + }, { useWasi: true }).then( + data => [null, data], + err => [err, null] + ); + + assert(plugin === null); + assert(/hash mismatch/.test(err.message)) + if (plugin) { + await plugin.close() + } +}) + +test('createPlugin fails on hash mismatch (hash mismatch)', async () => { + const [err, plugin] = await createPlugin({ + wasm: [{ + url: 'http://localhost:8124/wasm/code.wasm', + hash: '7def5bb4aa3843a5daf5d6078f1e8540e5ef10b035a9d9387e9bd5156d2b256a' + }] + }, { useWasi: true }).then( + data => [null, data], + err => [err, null] + ); + + assert(plugin === null); + assert(/hash mismatch/.test(err.message)) + if (plugin) { + await plugin.close() + } +}); + +test('createPlugin loads a module and provides access to exports/imports', async () => { + const plugin = await createPlugin({wasm: [ + { url: 'http://localhost:8124/wasm/code.wasm' } + ]}, { useWasi: true }); + + try { + const exports = await plugin.getExports(); + assert.deepEqual(exports.map(xs => xs.name).sort(), ['memory', 'count_vowels', '__data_end', '__heap_base'].sort()); + + const imports = await plugin.getImports(); + assert.deepEqual(imports.map(xs => xs.name).sort(), ['extism_alloc', + 'extism_output_set', + 'extism_input_length', + 'extism_input_load_u64', + 'extism_input_load_u8', + 'extism_store_u64', + 'extism_store_u8'].sort()); + } finally { + await plugin.close(); + } +}); + +test('createPlugin returns an interface that can call wasm functions', async () => { + const plugin = await createPlugin({wasm: [ + { url: 'http://localhost:8124/wasm/code.wasm' } + ]}, { useWasi: true }); + + try { + const result = await plugin.call('count_vowels', 'hello world'); + assert(result, 'result is not null'); + + assert.deepEqual(JSON.parse(new TextDecoder().decode(result.buffer)), { count: 3 }) + } finally { + await plugin.close(); + } +}); + +test('logging works as expected', async () => { + const intercept: Record = {}; + const logLevel = (level: string) => (message: string) => intercept[level] = message + + // FIXME: we're using non-blocking log functions here; to properly preserve behavior we + // should invoke these and wait on the host to return. + const logger = Object.fromEntries([ + 'info', + 'debug', + 'warn', + 'error' + ].map(lvl => [lvl, logLevel(lvl)])) as unknown as Console; + + const plugin = await createPlugin({wasm: [ + { url: 'http://localhost:8124/wasm/log.wasm' } + ]}, { useWasi: true, logger }); + + try { + await plugin.call('run_test', ''); + assert.deepEqual(intercept, { + debug: 'this is a debug log', + error: 'this is an erorr log', + info: 'this is an info log', + warn: 'this is a warning log' + }); + } finally { + await plugin.close(); + } +}); + +test('host functions may read info from context and return values', async () => { + let executed: any + const functions = { + env: { + hello_world (context: CallContext, off: bigint) { + executed = context.readString(off); + return context.store("wow okay then") + } + } + } + const plugin = await createPlugin({wasm: [ + { url: 'http://localhost:8124/wasm/code-functions.wasm' } + ]}, { useWasi: true, functions }); + + try { + const output = await plugin.call('count_vowels', 'hello world'); + assert.equal(new TextDecoder().decode(output as Uint8Array), "wow okay then") + assert.equal(executed, '{"count": 3}') + } finally { + await plugin.close(); + } +}) + +test('host functions reject original promise when throwing', async () => { + const expected = String(Math.random()) + const functions = { + env: { + hello_world (_context: CallContext, _off: bigint) { + throw new Error(expected) + } + } + } + const plugin = await createPlugin({wasm: [ + { url: 'http://localhost:8124/wasm/code-functions.wasm' } + ]}, { useWasi: true, functions }); + + try { + const [err, data] = await plugin.call('count_vowels', 'hello world').then( + data => [null, data], + err => [err, null] + ); + + assert(data === null); + assert.equal(err?.message, expected); + } finally { + await plugin.close(); + } +}) + +if (FEATURES.hasOffThreadCapability) { + test('host functions may be async if worker is off-main-thread', async () => { + const functions = { + env: { + async hello_world (context: CallContext, _off: bigint) { + await new Promise(resolve => setTimeout(resolve, 100)); + return context.store("it works") + } + } + } + + const plugin = await createPlugin({wasm: [ + { url: 'http://localhost:8124/wasm/code-functions.wasm' } + ]}, { useWasi: true, functions, offMainThread: true }); + + try { + const output = await plugin.call('count_vowels', 'hello world'); + assert.equal(new TextDecoder().decode(output as Uint8Array), "it works") + } finally { + await plugin.close(); + } + }) + + test('host functions may not be reentrant off-main-thread', async () => { + const functions = { + env: { + async hello_world (context: CallContext, _off: bigint) { + await plugin?.call('count_vowels', 'hello world') + return context.store("it works") + } + } + } + + const plugin = await createPlugin({wasm: [ + { url: 'http://localhost:8124/wasm/code-functions.wasm' } + ]}, { useWasi: true, functions, offMainThread: true }); + + try { + const [err, data] = await plugin.call('count_vowels', 'hello world').then( + data => [null, data], + err => [err, null] + ); + + assert(data === null); + assert.equal(err?.message, 'plugin is not reentrant'); + } finally { + await plugin.close(); + } + }) +} + +test('createPlugin fails as expected when calling unknown function', async () => { + const plugin = await createPlugin('http://localhost:8124/wasm/code.wasm', { useWasi: true }); + + try { + const [err, data] = await plugin.call('reticulate_splines', 'hello world').then( + data => [null, data], + err => [err, null] + ); + + assert(data === null); + assert.equal(err?.message, 'Plugin error: target "reticulate_splines" does not exist'); + } finally { + await plugin.close(); + } +}); + + +test('plugin can allocate memory', async () => { + const plugin = await createPlugin('http://localhost:8124/wasm/alloc.wasm'); + try { + await plugin.call('run_test', ''); + } finally { + await plugin.close(); + } +}); + +test('plugin can fail gracefully', async () => { + const plugin = await createPlugin('http://localhost:8124/wasm/fail.wasm'); + try { + const [err, data] = await plugin.call('run_test', '').then( + data => [null, data], + err => [err, null] + ); + assert(data === null); + assert.equal(err.message, 'Plugin-originated error: Some error message'); + } finally { + await plugin.close(); + } +}); + +test('can initialize Haskell runtime', async () => { + const plugin = await createPlugin('http://localhost:8124/wasm/hello_haskell.wasm', { + config: { greeting: 'Howdy' }, + useWasi: true + }); + + try { + let output = await plugin.call('testing', 'John'); + + let result = decode(output as Uint8Array); + assert.equal(result, 'Howdy, John'); + + output = await plugin.call('testing', 'Ben'); + assert(output !== null); + result = decode(output as Uint8Array); + assert.equal(result, 'Howdy, Ben'); + } finally { + await plugin.close(); + } +}); + +if (FEATURES.fsAccess) { + test('can access fs', async () => { + const plugin = await createPlugin('http://localhost:8124/wasm/fs.wasm', { + allowedPaths: { '/mnt': 'tests/data' }, + useWasi: true + }); + + try { + const output = await plugin.call('run_test', ''); + assert(output !== null); + const result = decode(output as Uint8Array); + assert.equal(result, 'hello world!'); + } finally { + await plugin.close(); + } + }); +} + +function decode(buffer: Uint8Array) { + const decoder = new TextDecoder(); + return decoder.decode(buffer); +} diff --git a/src/mod.ts b/src/mod.ts new file mode 100644 index 0000000..d1a2d3b --- /dev/null +++ b/src/mod.ts @@ -0,0 +1,128 @@ +export type { + Manifest, + ManifestWasm, + ManifestWasmUrl, + ManifestWasmData, + ManifestLike as IntoManifest +} from './manifest.ts'; +import { FEATURES } from 'js-sdk:features'; +export { FEATURES } from 'js-sdk:features'; + +import { + ManifestLike, + intoManifest as _intoManifest, + toWasmModuleData as _toWasmModuleData +} from './manifest.ts'; + +import { createForegroundPlugin as _createForegroundPlugin } from './foreground-plugin.ts'; +import { createBackgroundPlugin as _createBackgroundPlugin } from './background-plugin.ts'; +export { type CallContext } from './call-context.ts'; + +/** + * {@link ExtismPlugin} Config + */ +export interface PluginConfigLike { + [key: string]: string +} +export type PluginConfig = { [key: string]: string }; + +/** + * Options for initializing an Extism plugin. + */ +export interface ExtismPluginOptions { + useWasi?: boolean | undefined; + offMainThread?: boolean | undefined; + logger?: Console; + functions?: { [key: string]: { [key: string]: any } } | undefined; + allowedPaths?: { [key: string]: string } | undefined; + allowedHosts?: string[] | undefined; + config?: PluginConfigLike | undefined; + fetch?: typeof fetch; +} + +export interface Plugin { + /** + * Check if a function exists in the WebAssembly module. + * + * @param {string | [string, string]} funcName The function's name, or a tuple of target module name and function name. + * @returns {Promise} true if the function exists, otherwise false + */ + functionExists(funcName: string | [string, string]): Promise; + close(): Promise; + + /** + * Call a specific function from the WebAssembly module with provided input. + * + * @param {string | [string, string]} funcName The name of the function to call + * @param {Uint8Array | string} input The input to pass to the function + * @returns {Promise} The result from the function call + */ + call(funcName: string | [string, string], input?: string | number | Uint8Array): Promise; + callBlock(funcName: string | [string, string], input?: number | null): Promise<[number | null, number | null]>; + getExports(name?: string): Promise; + getImports(name?: string): Promise; + getInstance(name?: string): Promise; +} + +// TODO: move these into another module... +export interface InternalConfig { + logger: Console, + allowedHosts: string[], + allowedPaths: {[key: string]: string}, + functions: {[namespace: string]: {[func: string]: any}}, + fetch: typeof fetch, + wasiEnabled: boolean, + config: PluginConfig, +} + +export interface InternalWasi { + importObject(): Promise>; + initialize(instance: WebAssembly.Instance): Promise +} + +export default async function createPlugin( + manifest: ManifestLike | PromiseLike, + opts: ExtismPluginOptions = {} +): Promise { + manifest = await _intoManifest(await Promise.resolve(manifest)); + opts = {...opts}; + opts.useWasi ??= false; + opts.functions = opts.functions || {}; + opts.allowedPaths ??= {}; + opts.allowedHosts ??= [].concat(opts.allowedHosts || []); + opts.logger ??= console; + opts.config ??= {} + + opts.offMainThread ??= FEATURES.hasOffThreadCapability; + if (opts.offMainThread && !FEATURES.hasOffThreadCapability) { + throw new Error( + 'Cannot enable off-thread wasm; current context is not `crossOriginIsolated` (see https://mdn.io/crossOriginIsolated)' + ); + } + + const [names, moduleData] = await _toWasmModuleData(manifest, opts.fetch ?? fetch); + + const config = {...opts.config, ...manifest.config}; + const allowedHosts = [...(manifest.allowed_hosts || []), ...(opts.allowedHosts || [])]; + const allowedPaths = opts.allowedPaths || {}; + const functions = opts.functions || {}; + const logger = opts.logger || console; + const _fetch = opts.fetch || fetch; + const wasiEnabled = Boolean(opts.useWasi); + + const ic: InternalConfig = { + allowedHosts, + allowedPaths, + functions, + fetch: _fetch, + wasiEnabled, + logger, + config, + } + + return (opts.offMainThread ? _createBackgroundPlugin : _createForegroundPlugin)( + ic, + names, + moduleData, + ); +} diff --git a/src/node/index.ts b/src/node/index.ts deleted file mode 100644 index 8c6ad19..0000000 --- a/src/node/index.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { - ExtismPluginBase, - PluginWasi, - ExtismPluginOptions, - fetchModuleData, - instantiateExtismRuntime, - Manifest, - ManifestWasm, - ManifestWasmData, - ManifestWasmUrl, - HttpRequest, - HttpResponse, - embeddedRuntime, - embeddedRuntimeHash, - CurrentPlugin, - StreamingSource, - isURL, -} from '../plugin'; -import { WASI } from 'wasi'; -import { readFile } from 'fs'; -import { promisify } from 'util'; -import syncFetch from 'sync-fetch'; -import fetchPolyfill from 'node-fetch'; -import { minimatch } from 'minimatch'; -import { createHash } from 'crypto'; -import path from 'path'; - -class ExtismPlugin extends ExtismPluginBase { - protected supportsHttpRequests(): boolean { - return true; - } - - protected httpRequest(request: HttpRequest, body: Uint8Array | null): HttpResponse { - let b = body - ? { - buffer: body, - byteLength: body.length, - byteOffset: 0, - } - : undefined; - - if (request.method == 'GET' || request.method == 'HEAD') { - b = undefined; - } - - const response = syncFetch(request.url, { - headers: request.headers, - method: request.method, - body: b, - }); - - return { - body: new Uint8Array(response.arrayBuffer()), - status: response.status, - }; - } - - protected matches(text: string, pattern: string): boolean { - return minimatch(text, pattern); - } - - protected loadWasi(options: ExtismPluginOptions): PluginWasi { - const wasi = new WASI({ - //@ts-ignore - version: 'preview1', - preopens: options.allowedPaths, - }); - - return new PluginWasi(wasi, wasi.wasiImport, instance => this.initialize(wasi, instance)); - } - - private initialize(wasi: WASI, instance: WebAssembly.Instance) { - const memory = instance.exports.memory as WebAssembly.Memory; - - if (!memory) { - throw new Error("The module has to export a default memory.") - } - - wasi.start({ - exports: { - memory, - _start: () => { }, - }, - }); - } -} - -/** - * Create a new plugin. - * @param manifestData An Extism manifest {@link Manifest} or a Wasm module. - * @param options Options for initializing the plugin. - * @returns {ExtismPlugin} An initialized plugin. - */ -async function createPlugin( - manifestData: Manifest | ManifestWasm | ArrayBuffer | string | URL, - options: ExtismPluginOptions, -): Promise { - - if (typeof manifestData === "string" || manifestData instanceof URL) { - manifestData = { - url: manifestData as string | URL - } - } - - let moduleData = await fetchModuleData(manifestData, fetchWasm, calculateHash); - - const runtimeWasm = options.runtime ?? { - data: toBytes(embeddedRuntime), - hash: embeddedRuntimeHash - }; - - let runtime = await instantiateExtismRuntime(runtimeWasm, fetchWasm, calculateHash); - - return new ExtismPlugin(runtime, moduleData, options); - - async function fetchWasm(wasm: ManifestWasm): Promise { - if ((wasm as ManifestWasmData).data) { - return Promise.resolve((wasm as ManifestWasmData).data); - } else if ((wasm as ManifestWasmUrl).url) { - const url = (wasm as ManifestWasmUrl).url; - - let source: StreamingSource; - if (isURL(url)) { - const response = await fetch(url); - - //@ts-ignore - if (WebAssembly.instantiateStreaming) { - source = response; - } else { - source = await response.arrayBuffer(); - } - } else { - const readFileAsync = (path: string) => promisify(readFile)(path); - const buffer = await readFileAsync(url as string); - const array = new Uint8Array(buffer); - source = array.buffer.slice(array.byteOffset, array.byteOffset + array.byteLength); - } - - return source; - } else { - throw new Error(`Unrecognized wasm source: ${wasm}`); - } - } - - async function calculateHash(data: ArrayBuffer) { - const hasher = createHash('sha256'); - hasher.update(new Uint8Array(data)); - return new Promise((resolve, _) => resolve(hasher.digest('hex'))); - } - - function toBytes(base64: string): Uint8Array { - const buffer = Buffer.from(base64, 'base64'); - return new Uint8Array(buffer); - } -} - -export default createPlugin; -export type { ExtismPlugin, CurrentPlugin, ExtismPluginOptions, Manifest, ManifestWasm, ManifestWasmData, ManifestWasmUrl }; \ No newline at end of file diff --git a/src/plugin-options.ts b/src/plugin-options.ts new file mode 100644 index 0000000..b6677a8 --- /dev/null +++ b/src/plugin-options.ts @@ -0,0 +1,41 @@ +import { EMBEDDED_RUNTIME, EMBEDDED_RUNTIME_HASH } from './runtime.ts'; +import type { ManifestWasm } from './manifest.ts'; +import { FEATURES } from 'js-sdk:features'; + +/** + * Options for initializing an Extism plugin. + */ +export interface ExtismPluginOptions { + useWasi?: boolean | undefined; + runtime?: ManifestWasm | undefined; + offMainThread?: boolean | undefined; + logger?: Console; + functions?: { [key: string]: { [key: string]: any } } | undefined; + allowedPaths?: { [key: string]: string } | undefined; + allowedHosts?: string[] | undefined; + config?: PluginConfigLike | undefined; + fetch?: typeof fetch; +} + +export function intoPluginOptions(opts: ExtismPluginOptions): ExtismPluginOptions { + opts = {...opts}; + opts.useWasi ??= false; + opts.functions = opts.functions || {}; + opts.runtime ??= { + data: Uint8Array.from(atob(EMBEDDED_RUNTIME) as ArrayLike, (xs: string, _idx: number) => xs.codePointAt(0) as number), + hash: EMBEDDED_RUNTIME_HASH + } + opts.allowedPaths ??= {}; + opts.allowedHosts ??= [].concat(opts.allowedHosts || []); + opts.logger ??= console; + + opts.offMainThread ??= FEATURES.hasOffThreadCapability; + if (opts.offMainThread && !FEATURES.hasOffThreadCapability) { + throw new Error( + 'Cannot enable off-thread wasm; current context is not `crossOriginIsolated` (see https://mdn.io/crossOriginIsolated)' + ); + } + + opts.config = opts.config ?? {}; + return opts; +} diff --git a/src/plugin.ts b/src/plugin.ts deleted file mode 100644 index c69e159..0000000 --- a/src/plugin.ts +++ /dev/null @@ -1,704 +0,0 @@ -export abstract class ExtismPluginBase { - moduleData: ArrayBuffer | Response | PromiseLike; - currentPlugin: CurrentPlugin; - input: Uint8Array; - output: Uint8Array; - module?: WebAssembly.WebAssemblyInstantiatedSource; - options: ExtismPluginOptions; - lastStatusCode: number = 0; - guestRuntime: GuestRuntime; - - constructor(extism: WebAssembly.Instance, moduleData: ArrayBuffer | Response | PromiseLike, options: ExtismPluginOptions) { - this.moduleData = moduleData; - this.currentPlugin = new CurrentPlugin(this, extism); - this.input = new Uint8Array(); - this.output = new Uint8Array(); - this.options = options; - this.guestRuntime = { type: GuestRuntimeType.None, init: () => { }, initialized: true }; - } - - async getExports(): Promise { - const module = await this.instantiateModule(); - return module.instance.exports; - } - - async getImports(): Promise { - const module = await this.instantiateModule(); - return WebAssembly.Module.imports(module.module); - } - - async getInstance(): Promise { - const module = await this.instantiateModule(); - return module.instance; - } - - /** - * Check if a function exists in the WebAssembly module. - * - * @param {string} name The function's name - * @returns {Promise} true if the function exists, otherwise false - */ - async functionExists(name: string): Promise { - const module = await this.instantiateModule(); - return module.instance.exports[name] ? true : false; - } - - /** - * Call a specific function from the WebAssembly module with provided input. - * - * @param {string} func_name The name of the function to call - * @param {Uint8Array | string} input The input to pass to the function - * @returns {Promise} The result from the function call - */ - async call(func_name: string, input: Uint8Array | string): Promise { - const module = await this.instantiateModule(); - - if (typeof input === 'string') { - this.input = new TextEncoder().encode(input); - } else if (input instanceof Uint8Array) { - this.input = input; - } else { - throw new Error('Plugin error: input should be string or Uint8Array'); - } - - this.currentPlugin.reset(); - - let func = module.instance.exports[func_name]; - if (!func) { - throw Error(`Plugin error: function does not exist ${func_name}`); - } - - if (func_name != '_start' && this.guestRuntime?.init && !this.guestRuntime.initialized) { - this.guestRuntime.init(); - this.guestRuntime.initialized = true; - } - - //@ts-ignore - func(); - return this.output; - } - - protected abstract loadWasi(options: ExtismPluginOptions): PluginWasi; - - protected abstract supportsHttpRequests(): boolean; - - protected abstract httpRequest(request: HttpRequest, body: Uint8Array | null): HttpResponse; - - protected abstract matches(text: string, pattern: string): boolean; - - private async instantiateModule(): Promise { - if (this.module) { - return this.module; - } - - const environment = this.makeEnv(); - const pluginWasi = this.options.useWasi ? this.loadWasi(this.options) : undefined; - - let imports: any = { - wasi_snapshot_preview1: pluginWasi?.importObject(), - env: environment, - }; - - for (const m in this.options.functions) { - imports[m] = imports[m] || {}; - const map = this.options.functions[m]; - - for (const f in map) { - imports[m][f] = this.options.functions[m][f]; - } - } - - for (const m in imports) { - if (m === "wasi_snapshot_preview1") { - continue; - } - - for (const f in imports[m]) { - imports[m][f] = imports[m][f].bind(null, this.currentPlugin); - } - } - - if (this.moduleData instanceof ArrayBuffer || ArrayBuffer.isView(this.moduleData)) { - this.module = await WebAssembly.instantiate(this.moduleData, imports); - } else { - this.module = await WebAssembly.instantiateStreaming(this.moduleData, imports); - } - - if (this.module.instance.exports._start) { - pluginWasi?.initialize(this.module.instance); - } - - this.guestRuntime = detectGuestRuntime(this.module.instance); - - return this.module; - } - - private makeEnv(): any { - let plugin = this; - var env: any = { - extism_alloc(cp: CurrentPlugin, n: bigint): bigint { - const response = cp.alloc(n); - return response; - }, - extism_free(cp: CurrentPlugin, n: bigint) { - cp.free(n); - }, - extism_load_u8(cp: CurrentPlugin, n: bigint): number { - return cp.getMemoryBuffer()[Number(n)]; - }, - extism_load_u64(cp: CurrentPlugin, n: bigint): bigint { - let cast = new DataView(cp.getMemory().buffer, Number(n)); - return cast.getBigUint64(0, true); - }, - extism_store_u8(cp: CurrentPlugin, offset: bigint, n: number) { - cp.getMemoryBuffer()[Number(offset)] = Number(n); - }, - extism_store_u64(cp: CurrentPlugin, offset: bigint, n: bigint) { - const tmp = new DataView(cp.getMemory().buffer, Number(offset)); - tmp.setBigUint64(0, n, true); - }, - extism_input_length(): bigint { - return BigInt(plugin.input.length); - }, - extism_input_load_u8(cp: CurrentPlugin, i: bigint): number { - return plugin.input[Number(i)]; - }, - extism_input_load_u64(cp: CurrentPlugin, idx: bigint): bigint { - let cast = new DataView(plugin.input.buffer, Number(idx)); - return cast.getBigUint64(0, true); - }, - extism_output_set(cp: CurrentPlugin, offset: bigint, length: bigint) { - const offs = Number(offset); - const len = Number(length); - plugin.output = cp.getMemoryBuffer().slice(offs, offs + len); - }, - extism_error_set(cp: CurrentPlugin, i: bigint) { - throw new Error(`Call error: ${cp.readString(i)}`); - }, - extism_config_get(cp: CurrentPlugin, i: bigint): bigint { - if (typeof plugin.options.config === 'undefined') { - return BigInt(0); - } - const key = cp.readString(i); - if (key === null) { - return BigInt(0); - } - const value = plugin.options.config[key]; - if (typeof value === 'undefined') { - return BigInt(0); - } - return cp.writeString(value); - }, - extism_var_get(cp: CurrentPlugin, i: bigint): bigint { - const key = cp.readString(i); - if (key === null) { - return BigInt(0); - } - const value = cp.vars[key]; - if (typeof value === 'undefined') { - return BigInt(0); - } - return cp.writeBytes(value); - }, - extism_var_set(cp: CurrentPlugin, n: bigint, i: bigint) { - const key = cp.readString(n); - if (key === null) { - return; - } - const value = cp.readBytes(i); - if (value === null) { - return; - } - cp.vars[key] = value; - }, - extism_http_request(cp: CurrentPlugin, requestOffset: bigint, bodyOffset: bigint): bigint { - if (!plugin.supportsHttpRequests()) { - cp.free(bodyOffset); - cp.free(requestOffset); - throw new Error('Call error: http requests are not supported.'); - } - - const requestJson = cp.readString(requestOffset); - if (requestJson == null) { - throw new Error('Call error: Invalid request.'); - } - - var request: HttpRequest = JSON.parse(requestJson); - - // The actual code starts here - const url = new URL(request.url); - let hostMatches = false; - for (const allowedHost of (plugin.options.allowedHosts ?? [])) { - if (allowedHost === url.hostname) { - hostMatches = true; - break; - } - - // Using minimatch for pattern matching - const patternMatches = plugin.matches(url.hostname, allowedHost); - if (patternMatches) { - hostMatches = true; - break; - } - } - - if (!hostMatches) { - throw new Error(`Call error: HTTP request to '${request.url}' is not allowed`); - } - - // TODO: limit number of bytes read to 50 MiB - const body = cp.readBytes(bodyOffset); - cp.free(bodyOffset); - cp.free(requestOffset); - - const response = plugin.httpRequest(request, body); - plugin.lastStatusCode = response.status; - - const offset = cp.writeBytes(response.body); - - return offset; - }, - extism_http_status_code(): number { - return plugin.lastStatusCode; - }, - extism_length(cp: CurrentPlugin, i: bigint): bigint { - return cp.getLength(i); - }, - extism_log_warn(cp: CurrentPlugin, i: bigint) { - const s = cp.readString(i); - console.warn(s); - }, - extism_log_info(cp: CurrentPlugin, i: bigint) { - const s = cp.readString(i); - console.log(s); - }, - extism_log_debug(cp: CurrentPlugin, i: bigint) { - const s = cp.readString(i); - console.debug(s); - }, - extism_log_error(cp: CurrentPlugin, i: bigint) { - const s = cp.readString(i); - console.error(s); - }, - }; - - return env; - } -} - -/** - * Represents the raw bytes of a WASM file loaded into memory - */ -export type ManifestWasmData = { - data: Uint8Array; - name?: string; - hash?: string; -}; - -/** - * Represents a url to a WASM module - */ -export type ManifestWasmUrl = { - url: URL | string; - name?: string; - hash?: string; -}; - -/** - * {@link ExtismPlugin} Config - */ -export type PluginConfig = { [key: string]: string }; - -/** - * The WASM to load as bytes, a path, or a url - */ -export type ManifestWasm = ManifestWasmUrl | ManifestWasmData; - -/** - * The manifest which describes the {@link ExtismPlugin} code and - * runtime constraints. - * - * @see [Extism > Concepts > Manifest](https://extism.org/docs/concepts/manifest) - */ -export type Manifest = { - wasm: Array; - //memory?: ManifestMemory; - config?: PluginConfig; - allowed_hosts?: Array; -}; - -/** - * Options for initializing an Extism plugin. - */ -export interface ExtismPluginOptions { - useWasi?: boolean | undefined; - runtime?: ManifestWasm | undefined; - functions?: { [key: string]: { [key: string]: any } } | undefined; - allowedPaths?: { [key: string]: string } | undefined; - allowedHosts?: string[] | undefined; - config?: PluginConfig | undefined; -} - -/** - * Provides a unified interface for the supported WASI implementations. - */ -export class PluginWasi { - wasi: any; - imports: any; - #initialize: (instance: WebAssembly.Instance) => void; - - constructor(wasi: any, imports: any, init: (instance: WebAssembly.Instance) => void) { - this.wasi = wasi; - this.imports = imports; - this.#initialize = init; - } - - importObject() { - return this.imports; - } - - initialize(instance: WebAssembly.Instance) { - this.#initialize(instance); - } -} - -enum GuestRuntimeType { - None, - Haskell, - Wasi, -} - -type GuestRuntime = { - init: () => void; - initialized: boolean; - type: GuestRuntimeType; -}; - -export type StreamingSource = ArrayBuffer | Response - -export function isURL(url: URL | string) { - return url instanceof URL || url.includes("://"); -} - -export async function fetchModuleData( - manifestData: Manifest | ManifestWasm | ArrayBuffer, - fetchWasm: (wasm: ManifestWasm) => Promise, - calculateHash: (buffer: ArrayBuffer) => Promise, -) { - let moduleData: StreamingSource | null = null; - - if (manifestData instanceof ArrayBuffer) { - moduleData = manifestData; - } else if ((manifestData as Manifest).wasm) { - const wasmData = (manifestData as Manifest).wasm; - if (wasmData.length > 1) throw Error('This runtime only supports one module in Manifest.wasm'); - - const wasm = wasmData[0]; - moduleData = await fetchWasm(wasm); - } else if ( - (manifestData as ManifestWasmData).data || - (manifestData as ManifestWasmUrl).url - ) { - moduleData = await fetchWasm(manifestData as ManifestWasm); - - if (moduleData instanceof Response) { - // HACK: WebAssembly.instantiateStreaming only works when the content-type is application/wasm - // This can be problematic because a lot of object storages store the content-type - // as application/octet-stream by default - if (moduleData.headers.get('Content-Type') === 'application/octet-stream') { - const headers = new Headers(moduleData.headers); - headers.set('Content-Type', 'application/wasm'); - - moduleData = new Response(moduleData.body, { - status: moduleData.status, - statusText: moduleData.statusText, - headers: headers, - }) - } - } - - const expected = (manifestData as ManifestWasm).hash; - - if (expected) { - let actual: string; - if (moduleData instanceof ArrayBuffer || moduleData instanceof Uint8Array) { - actual = await calculateHash(moduleData); - } else { - // Download the content to check its hash - moduleData = await moduleData.arrayBuffer(); - actual = await calculateHash(moduleData); - } - - if (actual != expected) { - throw new Error(`Plugin error: hash mismatch. Expected: ${expected}. Actual: ${actual}`); - } - } - } - - if (!moduleData) { - throw Error(`Unsure how to interpret manifest ${(manifestData as any).path}`); - } - - return moduleData; -} - -function haskellRuntime(module: WebAssembly.Instance): GuestRuntime | null { - const haskellInit = module.exports.hs_init; - - if (!haskellInit) { - return null; - } - - const reactorInit = module.exports._initialize; - - let init: () => void; - if (reactorInit) { - //@ts-ignore - init = () => reactorInit(); - } else { - //@ts-ignore - init = () => haskellInit(); - } - - const kind = reactorInit ? 'reactor' : 'normal'; - console.debug(`Haskell (${kind}) runtime detected.`); - - return { type: GuestRuntimeType.Haskell, init: init, initialized: false }; -} - -function wasiRuntime(module: WebAssembly.Instance): GuestRuntime | null { - const reactorInit = module.exports._initialize; - const commandInit = module.exports.__wasm_call_ctors; - - // WASI supports two modules: Reactors and Commands - // we prioritize Reactors over Commands - // see: https://github.com/WebAssembly/WASI/blob/main/legacy/application-abi.md - - let init: () => void; - if (reactorInit) { - //@ts-ignore - init = () => reactorInit(); - } else if (commandInit) { - //@ts-ignore - init = () => commandInit(); - } else { - return null; - } - - const kind = reactorInit ? 'reactor' : 'command'; - console.debug(`WASI (${kind}) runtime detected.`); - - return { type: GuestRuntimeType.Wasi, init: init, initialized: false }; -} - -function detectGuestRuntime(module: WebAssembly.Instance): GuestRuntime { - const none = { init: () => { }, type: GuestRuntimeType.None, initialized: true }; - return haskellRuntime(module) ?? wasiRuntime(module) ?? none; -} - -export async function instantiateExtismRuntime( - runtime: ManifestWasm | null, - fetchWasm: (wasm: ManifestWasm) => Promise, - calculateHash: (buffer: ArrayBuffer) => Promise, -): Promise { - if (!runtime) { - throw Error('Please specify Extism runtime.'); - } - - const extismWasm = await fetchModuleData(runtime, fetchWasm, calculateHash); - - let extismInstance: WebAssembly.Instance; - if (extismWasm instanceof Response) { - extismInstance = (await WebAssembly.instantiateStreaming(extismWasm)).instance; - } else { - extismInstance = (await WebAssembly.instantiate(extismWasm)).instance; - } - - return extismInstance; -} - -export type HttpResponse = { - body: Uint8Array; - status: number; -}; - -export type HttpRequest = { - url: string; - headers: { [key: string]: string }; - method: string; -}; - -export const embeddedRuntime = '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'; - -export const embeddedRuntimeHash = 'aab40a25b5edda3fc5c9d1de1bf17af9ce5d02cbac1bacf5bdfa495b4ab9fc39' - -export class CurrentPlugin { - vars: Record; - plugin: ExtismPluginBase; - #extism: WebAssembly.Instance; - - constructor(plugin: ExtismPluginBase, extism: WebAssembly.Instance) { - this.vars = {}; - this.plugin = plugin; - this.#extism = extism; - } - - setVar(name: string, value: Uint8Array | string | number): void { - if (value instanceof Uint8Array) { - this.vars[name] = value; - } else if (typeof value === 'string') { - this.vars[name] = new TextEncoder().encode(value); - } else if (typeof value === 'number') { - this.vars[name] = this.uintToLEBytes(value); - } else { - const typeName = (value as any)?.constructor.name || (value === null ? 'null' : typeof value); - throw new TypeError(`Invalid plugin variable type. Expected Uint8Array, string, or number, got ${typeName}`); - } - } - - readStringVar(name: string): string { - return new TextDecoder().decode(this.getVar(name)); - } - - getNumberVar(name: string): number { - const value = this.getVar(name); - if (value.length < 4) { - throw new Error(`Variable "${name}" has incorrect length`); - } - - return this.uintFromLEBytes(value); - } - - getVar(name: string): Uint8Array { - const value = this.vars[name]; - if (!value) { - throw new Error(`Variable ${name} not found`); - } - - return value; - } - - private uintToLEBytes(num: number): Uint8Array { - const bytes = new Uint8Array(4); - new DataView(bytes.buffer).setUint32(0, num, true); - - return bytes; - } - - private uintFromLEBytes(bytes: Uint8Array): number { - return new DataView(bytes.buffer).getUint32(0, true); - } - - /** - * Resets Extism memory. - * @returns {void} - */ - reset() { - return (this.#extism.exports.extism_reset as Function)(); - } - - /** - * Allocates a block of memory. - * @param {bigint} length - Size of the memory block. - * @returns {bigint} Offset in the memory. - */ - alloc(length: bigint): bigint { - return (this.#extism.exports.extism_alloc as Function)(length); - } - - /** - * Retrieves Extism memory. - * @returns {WebAssembly.Memory} The memory object. - */ - getMemory(): WebAssembly.Memory { - return this.#extism.exports.memory as WebAssembly.Memory; - } - - /** - * Retrieves Extism memory buffer as Uint8Array. - * @returns {Uint8Array} The buffer view. - */ - getMemoryBuffer(): Uint8Array { - return new Uint8Array(this.getMemory().buffer); - } - - /** - * Gets bytes from a specific memory offset. - * @param {bigint} offset - Memory offset. - * @returns {Uint8Array | null} Byte array or null if offset is zero. - */ - readBytes(offset: bigint): Uint8Array | null { - if (offset == BigInt(0)) { - return null; - } - - const length = this.getLength(offset); - - const buffer = new Uint8Array(this.getMemory().buffer, Number(offset), Number(length)); - - // Copy the buffer because `this.getMemory().buffer` returns a write-through view - return new Uint8Array(buffer); - } - - /** - * Retrieves a string from a specific memory offset. - * @param {bigint} offset - Memory offset. - * @returns {string | null} Decoded string or null if offset is zero. - */ - readString(offset: bigint): string | null { - const bytes = this.readBytes(offset); - if (bytes === null) { - return null; - } - - return new TextDecoder().decode(bytes); - } - - /** - * Allocates bytes to the WebAssembly memory. - * @param {Uint8Array} data - Byte array to allocate. - * @returns {bigint} Memory offset. - */ - writeBytes(data: Uint8Array): bigint { - const offs = this.alloc(BigInt(data.length)); - const buffer = new Uint8Array(this.getMemory().buffer, Number(offs), data.length); - buffer.set(data); - return offs; - } - - /** - * Allocates a string to the WebAssembly memory. - * @param {string} data - String to allocate. - * @returns {bigint} Memory offset. - */ - writeString(data: string): bigint { - const bytes = new TextEncoder().encode(data); - return this.writeBytes(bytes); - } - - /** - * Retrieves the length of a memory block from a specific offset. - * @param {bigint} offset - Memory offset. - * @returns {bigint} Length of the memory block. - */ - getLength(offset: bigint): bigint { - return (this.#extism.exports.extism_length as Function)(offset); - } - - inputLength(): bigint { - return (this.#extism.exports.extism_input_length as Function)(); - } - - /** - * Frees a block of memory from a specific offset. - * @param {bigint} offset - Memory offset to free. - * @returns {void} - */ - free(offset: bigint) { - if (offset == BigInt(0)) { - return; - } - - (this.#extism.exports.extism_free as Function)(offset); - } -} \ No newline at end of file diff --git a/src/polyfills/browser:features.ts b/src/polyfills/browser:features.ts new file mode 100644 index 0000000..97512e9 --- /dev/null +++ b/src/polyfills/browser:features.ts @@ -0,0 +1,19 @@ +export const FEATURES = { + // When false, shared buffers have to be copied to an array + // buffer before passing to Text{En,De}coding() + allowSharedBufferCodec: false, + + // Whether or not the manifest supports the "path:" key. + manifestSupportsPaths: false, + + // Whether or not cross-origin checks are enforced on this platform. + crossOriginChecksEnforced: true, + + fsAccess: false, + + hasOffThreadCapability: ( + typeof window !== 'undefined' + ? (window as any).crossOriginIsolated && typeof SharedArrayBuffer !== 'undefined' + : true + ), +} diff --git a/src/polyfills/browser:fs.ts b/src/polyfills/browser:fs.ts new file mode 100644 index 0000000..7725a1e --- /dev/null +++ b/src/polyfills/browser:fs.ts @@ -0,0 +1,3 @@ +export async function readFile(_path: string): Promise { + throw new Error('readFile not supported in this environment') +} diff --git a/src/polyfills/browser:wasi.ts b/src/polyfills/browser:wasi.ts new file mode 100644 index 0000000..7024a73 --- /dev/null +++ b/src/polyfills/browser:wasi.ts @@ -0,0 +1,35 @@ +import { WASI, Fd, File, OpenFile } from '@bjorn3/browser_wasi_shim'; +import { type InternalWasi } from '../mod.ts'; + +export async function loadWasi(_allowedPaths: {[from: string]: string}): Promise { + const args: Array = []; + const envVars: Array = []; + const fds: Fd[] = [ + new OpenFile(new File([])), // stdin + new OpenFile(new File([])), // stdout + new OpenFile(new File([])), // stderr + ]; + + const context = new WASI(args, envVars, fds); + + return { + async importObject() { + return context.wasiImport + }, + + async initialize(instance: WebAssembly.Instance) { + const memory = instance.exports.memory as WebAssembly.Memory; + + if (!memory) { + throw new Error('The module has to export a default memory.'); + } + + context.start({ + exports: { + memory, + _start: () => { }, + }, + }); + } + } +} diff --git a/src/polyfills/deno:features.ts b/src/polyfills/deno:features.ts new file mode 100644 index 0000000..7568f4e --- /dev/null +++ b/src/polyfills/deno:features.ts @@ -0,0 +1,15 @@ +export const FEATURES = { + // When false, shared buffers have to be copied to an array + // buffer before passing to Text{En,De}coding() + allowSharedBufferCodec: true, + + // Whether or not the manifest supports the "path:" key. + manifestSupportsPaths: true, + + // Whether or not cross-origin checks are enforced on this platform. + crossOriginChecksEnforced: false, + + fsAccess: true, + + hasOffThreadCapability: true, +} diff --git a/src/polyfills/deno:wasi.ts b/src/polyfills/deno:wasi.ts new file mode 100644 index 0000000..c580f7e --- /dev/null +++ b/src/polyfills/deno:wasi.ts @@ -0,0 +1,30 @@ +import Context from 'https://deno.land/std@0.200.0/wasi/snapshot_preview1.ts'; +import { type InternalWasi } from '../mod.ts'; + +export async function loadWasi(allowedPaths: {[from: string]: string}): Promise { + const context = new Context({ + preopens: allowedPaths, + exitOnReturn: false, + }); + + return { + async importObject() { + return context.exports + }, + + async initialize(instance: WebAssembly.Instance) { + const memory = instance.exports.memory as WebAssembly.Memory; + + if (!memory) { + throw new Error('The module has to export a default memory.'); + } + + context.start({ + exports: { + memory, + _start: () => { }, + }, + }); + } + } +} diff --git a/src/polyfills/host:node:worker_threads.ts b/src/polyfills/host:node:worker_threads.ts new file mode 100644 index 0000000..7c7f65c --- /dev/null +++ b/src/polyfills/host:node:worker_threads.ts @@ -0,0 +1,34 @@ +// This is a polyfill for the main thread in a browser context. +// We're making the native Worker API look like node's worker_threads +// implementation. +export const parentPort = null; + +const HANDLER_MAP = new WeakMap(); + +export class Worker extends (global.Worker || Object) { + constructor(url: string) { + super(url, { type: 'module', credentials: 'omit', name: 'extism-worker', crossOriginIsolated: true } as any) + } + + on(ev: string, action: any) { + const handler = (ev: any) => action(ev.data); + HANDLER_MAP.set(action, handler); + this.addEventListener(ev, handler); + } + + removeListener(ev: string, action: any) { + const handler = HANDLER_MAP.get(action); + if (handler) { + this.removeEventListener(ev, handler); + } + } + + once(ev: string, action: any) { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + this.addEventListener(ev, function handler(...args) { + self.removeEventListener(ev, handler); + action.call(self, ...args); + }); + } +} diff --git a/src/polyfills/node:features.ts b/src/polyfills/node:features.ts new file mode 100644 index 0000000..22b9f87 --- /dev/null +++ b/src/polyfills/node:features.ts @@ -0,0 +1,15 @@ +export const FEATURES = { + // When false, shared buffers have to be copied to an array + // buffer before passing to Text{En,De}coding() + allowSharedBufferCodec: false, + + // Whether or not the manifest supports the "path:" key. + manifestSupportsPaths: true, + + // Whether or not cross-origin checks are enforced on this platform. + crossOriginChecksEnforced: false, + + fsAccess: true, + + hasOffThreadCapability: true, +} diff --git a/src/polyfills/node:fs.ts b/src/polyfills/node:fs.ts new file mode 100644 index 0000000..8122e85 --- /dev/null +++ b/src/polyfills/node:fs.ts @@ -0,0 +1 @@ +export { readFile } from 'node:fs/promises'; diff --git a/src/polyfills/node:wasi.ts b/src/polyfills/node:wasi.ts new file mode 100644 index 0000000..7a94404 --- /dev/null +++ b/src/polyfills/node:wasi.ts @@ -0,0 +1,30 @@ +import { WASI } from 'wasi'; +import { type InternalWasi } from '../mod.ts'; + +export async function loadWasi(allowedPaths: {[from: string]: string}): Promise { + const context = new WASI({ + version: 'preview1', + preopens: allowedPaths, + } as any); + + return { + async importObject() { + return context.wasiImport + }, + + async initialize(instance: WebAssembly.Instance) { + const memory = instance.exports.memory as WebAssembly.Memory; + + if (!memory) { + throw new Error('The module has to export a default memory.'); + } + + context.start({ + exports: { + memory, + _start: () => { }, + }, + }); + } + } +} diff --git a/src/polyfills/worker:node:worker_threads.ts b/src/polyfills/worker:node:worker_threads.ts new file mode 100644 index 0000000..4aa1260 --- /dev/null +++ b/src/polyfills/worker:node:worker_threads.ts @@ -0,0 +1,16 @@ +// This is a polyfill for the worker thread in a browser context. +// We're exposing the worker thread's addEventListener/postMessage +// functionality out on something that looks like Node's MessagePort. +const _parentPort = null; + +export const parentPort = _parentPort || { + on (ev: string, fn: unknown) { + addEventListener(ev, (event: MessageEvent) => { + fn(event.data); + }) + }, + + postMessage (data, txf=[]) { + self.postMessage(data, txf); + } +} diff --git a/src/runtime.ts b/src/runtime.ts new file mode 100644 index 0000000..cf2b61a --- /dev/null +++ b/src/runtime.ts @@ -0,0 +1,3 @@ +// This file is generated by the justfile tooling. See "Developing" in README.md. +export const EMBEDDED_RUNTIME = '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'; +export const EMBEDDED_RUNTIME_HASH = 'aab40a25b5edda3fc5c9d1de1bf17af9ce5d02cbac1bacf5bdfa495b4ab9fc39' diff --git a/src/worker-url.ts b/src/worker-url.ts new file mode 100644 index 0000000..53490a8 --- /dev/null +++ b/src/worker-url.ts @@ -0,0 +1,2 @@ +// This file is aliased by esbuild for commonjs, esm, and browser builds. +export const WORKER_URL = new URL("./worker.ts", import.meta.url); diff --git a/src/worker.ts b/src/worker.ts new file mode 100644 index 0000000..9821917 --- /dev/null +++ b/src/worker.ts @@ -0,0 +1,304 @@ +import { parentPort } from 'node:worker_threads'; + +import { createForegroundPlugin as _createForegroundPlugin } from './foreground-plugin.ts'; +import { CallContext, EXPORT_STATE, CallState, IMPORT_STATE } from './call-context.ts'; +import { type Plugin, type InternalConfig } from './mod.ts'; + +// TODO: make this configurable +const MAX_WAIT = 5000; + +class Reactor { + hostFlag: Int32Array | null + sharedData: SharedArrayBuffer | null + sharedDataView: DataView | null + plugin?: Plugin + port: Exclude; + dynamicHandlers: Map Promise>; + context?: CallContext; + + constructor(port: typeof parentPort) { + if (!port) { + throw new Error('This should be unreachable: this module should only be invoked as a web worker.'); + } + + this.sharedData = null; + this.sharedDataView = null; + this.hostFlag = null; + this.port = port; + this.port.on('message', (ev: any) => this.handleMessage(ev)); + this.port.postMessage({ type: 'initialized' }); + + this.dynamicHandlers = new Map(); + this.dynamicHandlers.set('call', async ( + transfer: any[], + name: string | [string, string], + input: number | null, + state: CallState + ) => { + if (!this.context) { + throw new Error('invalid state: no context available to worker reactor'); + } + + this.context[IMPORT_STATE](state); + + const results: any = await this.plugin?.callBlock(name, input).then( + indices => [null, indices], + err => [err, null] + ); + + state = this.context[EXPORT_STATE]() + for (const [block,] of state.blocks) { + if (block) { + transfer.push(block) + } + } + + if (results[0]) { + results[0] = { + originalStack: results[0]?.stack, + message: results[0]?.message + } + } + + return { results, state } + }); + + this.dynamicHandlers.set('getExports', async (_txf, name) => { + return this.plugin?.getExports(name); + }); + + this.dynamicHandlers.set('getImports', async (_txf, name) => { + return this.plugin?.getImports(name); + }); + + this.dynamicHandlers.set('functionExists', async (_txf, name) => { + return this.plugin?.functionExists(name); + }); + } + + async handleMessage(ev: any) { + switch (ev.type) { + case 'init': return await this.handleInit(ev); + case 'invoke': return await this.handleInvoke(ev); + } + } + + async handleInvoke(ev: { + handler: string, + args: any[] + }) { + const handler = this.dynamicHandlers.get(ev.handler); + if (!handler) { + return this.port.postMessage({ + type: 'return', + result: [`no handler registered for ${ev.handler}`, null] + }) + } + + const transfer: any[] = []; + const results = await handler(transfer, ...ev.args || []).then( + ok => [null, ok], + err => [err, null] + ); + + if (results[0]) { + results[0] = { + originalStack: results[0]?.stack, + message: results[0]?.message + } + } + + return this.port.postMessage({ + type: 'return', + results + }, transfer) + } + + async handleInit(ev: InternalConfig & { + type: string, + names: string[], + modules: ArrayBuffer[], + sharedData: SharedArrayBuffer, + functions: {[name: string]: string[]} + }) { + this.sharedData = ev.sharedData; + this.sharedDataView = new DataView(ev.sharedData); + this.hostFlag = new Int32Array(this.sharedData); + + const functions = Object.fromEntries(Object.entries(ev.functions).map(([namespace, funcs]) => { + return [namespace, Object.fromEntries(funcs.map(funcName => { + return [funcName, (context: CallContext, ...args: any[]) => this.callHost(context, namespace, funcName, args)] + }))] + })) + + const { type: _, modules, functions: __, ...opts } = ev; + + const logLevel = (level: string) => (message: string) => this.port.postMessage({ type: 'log', level, message }) + + // TODO: we're using non-blocking log functions here; to properly preserve behavior we + // should invoke these and wait on the host to return. + const logger = Object.fromEntries([ + 'info', + 'debug', + 'warn', + 'error' + ].map(lvl => [lvl, logLevel(lvl)])) as unknown as Console; + + this.context = new CallContext(ArrayBuffer, logger, ev.config); + // TODO: replace our internal fetch and logger + this.plugin = await _createForegroundPlugin( + {...opts, functions, fetch, logger} as InternalConfig, + ev.names, + modules, + this.context + ); + + this.port.postMessage({ type: 'ready' }); + } + + callHost( + context: CallContext, + namespace: string, + func: string, + args: (number | bigint)[] + ): number | bigint | void { + if (!this.hostFlag) { + throw new Error('attempted to call host before receiving shared array buffer'); + } + + const state = context[EXPORT_STATE](); + + Atomics.store(this.hostFlag, 0, 0); + this.port.postMessage({ + type: 'invoke', + namespace, + func, + args, + state, + }) + + const reader = new RingBufferReader(this.sharedData as SharedArrayBuffer) + const blocks: [ArrayBufferLike | null, number][] = [] + let retval: any; + reader.pull() + + do { + switch (reader.readUint8()) { + + // end + case 0: + state.blocks = blocks; + context[IMPORT_STATE](state); + Atomics.store(this.hostFlag, 0, 0); + Atomics.notify(this.hostFlag, 0); + return retval; + + // ret i64 + case 1: + retval = reader.readUint64(); + break; + + // ret f64 + case 2: + retval = reader.readFloat64(); + break; + + // ret void + case 3: + retval = undefined + break; + + // block + case 4: { + const index = reader.readUint32(); + const len = reader.readUint32(); + if (!len) { + blocks.push([null, index]) + } else { + const output = new Uint8Array(len) + reader.read(output) + blocks.push([output.buffer, index]) + } + } break; + + // error + case 255: + break; + } + } while (1) + } +} + +const _CONTEXT = new Reactor(parentPort); + +class RingBufferReader { + input: SharedArrayBuffer + flag: Int32Array + inputOffset: number + scratch: ArrayBuffer + scratchView: DataView + + constructor(input: SharedArrayBuffer) { + this.input = input + this.inputOffset = 4 + this.flag = new Int32Array(this.input) + this.scratch = new ArrayBuffer(8) + this.scratchView = new DataView(this.scratch) + } + + get available() { + return this.flag[0] + } + + pull() { + if (Atomics.wait(this.flag, 0, 0, MAX_WAIT) === 'timed-out') { + throw new Error(`Worker timed out waiting for response from host after ${MAX_WAIT}ms`) + } + } + + read(output: Uint8Array) { + if (output.byteLength < this.available) { + output.set(new Uint8Array(this.input).subarray(this.inputOffset, this.inputOffset + output.byteLength)) + this.inputOffset += output.byteLength + return + } + + let outputOffset = 0 + let inputOffset = this.inputOffset + let extent = this.available - outputOffset + // read ::= [outputoffset, inputoffset, extent] + // firstread = [this.outputOffset, 0, this.available - this.outputOffset] + do { + output.set(new Uint8Array(this.input).subarray(inputOffset, inputOffset + extent), outputOffset) + outputOffset += extent + Atomics.store(this.flag, 0, 0); + Atomics.notify(this.flag, 0); + this.pull() + inputOffset = 4 + extent = Math.min(this.available - inputOffset, output.byteLength - outputOffset) + } while(outputOffset !== output.byteLength) + + this.inputOffset = inputOffset + } + + readUint8(): number { + this.read(new Uint8Array(this.scratch).subarray(0, 1)) + return this.scratchView.getUint8(0) + } + + readUint32(): number { + this.read(new Uint8Array(this.scratch).subarray(0, 4)) + return this.scratchView.getUint32(0, true) + } + + readUint64(): bigint { + this.read(new Uint8Array(this.scratch)) + return this.scratchView.getBigUint64(0, true) + } + + readFloat64(): number { + this.read(new Uint8Array(this.scratch)) + return this.scratchView.getFloat64(0, true) + } +} + diff --git a/tests/index.test.ts b/tests/index.test.ts deleted file mode 100644 index b9bcb63..0000000 --- a/tests/index.test.ts +++ /dev/null @@ -1,212 +0,0 @@ -import createPlugin, { CurrentPlugin, ExtismPlugin, ExtismPluginOptions, Manifest, ManifestWasm } from '../src/node/index'; - -async function newPlugin( - moduleName: string | Manifest | ManifestWasm | Buffer, - optionsConfig?: (opts: ExtismPluginOptions) => void): Promise { - const options: ExtismPluginOptions = { - useWasi: true, - runtime: { - url: 'wasm/extism-runtime.wasm', - }, - } - - if (optionsConfig) { - optionsConfig(options); - } - - let module: Manifest | ManifestWasm | Buffer; - if (typeof moduleName == 'string') { - module = { - url: `wasm/${moduleName}`, - }; - } else { - module = moduleName; - } - - const plugin = await createPlugin(module, options); - return plugin; -} - -function decode(buffer: Uint8Array) { - const decoder = new TextDecoder(); - return decoder.decode(buffer); -} - -describe('test extism', () => { - test('can create plugin from string', async () => { - const plugin = await createPlugin("wasm/code.wasm", { - useWasi: true - }); - - expect(await plugin.functionExists('count_vowels')).toBe(true); - }); - - test('can create plugin from url with hash check', async () => { - const plugin = await newPlugin({ - url: "https://raw.githubusercontent.com/extism/extism/main/wasm/code.wasm", - hash: "7def5bb4aa3843a5daf5d6078f1e8540e5ef10b035a9d9387e9bd5156d2b2565" - }); - - expect(await plugin.functionExists('count_vowels')).toBe(true); - }); - - test('can create plugin from url', async () => { - const plugin = await newPlugin({ - url: "https://raw.githubusercontent.com/extism/extism/main/wasm/code.wasm", - }); - - expect(await plugin.functionExists('count_vowels')).toBe(true); - }); - - test('fails on hash mismatch', async () => { - await expect(newPlugin({ - url: "wasm/code.wasm", - name: "code", - hash: "-----------" - })).rejects.toThrow(/Plugin error/); - }); - - test('can use embedded runtime', async () => { - let module = { - url: `wasm/code.wasm`, - }; - - const plugin = await createPlugin(module, { - useWasi: true - }); - - let output = await plugin.call('count_vowels', 'this is a test'); - - let result = JSON.parse(decode(output)); - expect(result['count']).toBe(4); - }); - - test('can create and call a plugin', async () => { - const plugin = await newPlugin('code.wasm'); - let output = await plugin.call('count_vowels', 'this is a test'); - - let result = JSON.parse(decode(output)); - expect(result['count']).toBe(4); - output = await plugin.call('count_vowels', 'this is a test again'); - result = JSON.parse(decode(output)); - expect(result['count']).toBe(7); - output = await plugin.call('count_vowels', 'this is a test thrice'); - result = JSON.parse(decode(output)); - expect(result['count']).toBe(6); - output = await plugin.call('count_vowels', '🌎hello🌎world🌎'); - result = JSON.parse(decode(output)); - expect(result['count']).toBe(3); - }); - - test('can detect if function exists or not', async () => { - const plugin = await newPlugin('code.wasm'); - expect(await plugin.functionExists('count_vowels')).toBe(true); - expect(await plugin.functionExists('i_dont_extist')).toBe(false); - }); - - test('errors when function is not known', async () => { - const plugin = await newPlugin('code.wasm'); - await expect(plugin.call('i_dont_exist', 'example-input')).rejects.toThrow(); - }); - - test('plugin can allocate memory', async () => { - const plugin = await newPlugin('alloc.wasm'); - await plugin.call("run_test", "") - }); - - test('plugin can fail gracefuly', async () => { - const plugin = await newPlugin('fail.wasm'); - await expect(() => plugin.call("run_test", "")).rejects.toThrowError(/Call error/); - }); - - test('host functions works', async () => { - const plugin = await newPlugin('code-functions.wasm', options => { - options.functions = { - "env": { - "hello_world": function (cp: CurrentPlugin, off: bigint) { - const result = JSON.parse(cp.readString(off) ?? ''); - result['message'] = 'hello from host!'; - return plugin.currentPlugin.writeString(JSON.stringify(result)); - } - } - } - }); - - const output = await plugin.call('count_vowels', 'aaa'); - const result = JSON.parse(decode(output)); - - expect(result).toStrictEqual({ - count: 3, - message: "hello from host!" - }) - }); - - test('can deny http requests', async () => { - const plugin = await newPlugin('http.wasm'); - await expect(() => plugin.call("run_test", "")).rejects.toThrowError(/Call error/); - }); - - test('can allow http requests', async () => { - const plugin = await newPlugin('http.wasm', options => { - options.allowedHosts = ['*.typicode.com']; - }); - - const output = await plugin.call("run_test", ""); - const result = JSON.parse(decode(output)); - - expect(result.id).toBe(1); - }); - - test('can log messages', async () => { - console.log = jest.fn(); - console.warn = jest.fn(); - console.error = jest.fn(); - console.debug = jest.fn(); - - const plugin = await newPlugin('log.wasm', options => { - options.allowedHosts = ['*.typicode.com']; - }); - - const _ = await plugin.call("run_test", ""); - - expect(console.log).toHaveBeenCalledWith("this is an info log"); - expect(console.warn).toHaveBeenCalledWith("this is a warning log"); - expect(console.error).toHaveBeenCalledWith("this is an erorr log"); - expect(console.debug).toHaveBeenCalledWith("this is a debug log"); - }); - - test('can initialize haskell runtime', async () => { - console.trace = jest.fn(); - - const plugin = await newPlugin('hello_haskell.wasm', options => { - options.config = { 'greeting': 'Howdy' }; - }); - - { - const output = await plugin.call("testing", "John"); - const result = decode(output); - - expect(result).toBe("Howdy, John") - } - - { - const output = await plugin.call("testing", "Ben"); - const result = decode(output); - - expect(result).toBe("Howdy, Ben") - } - - expect(console.debug).toHaveBeenCalledWith("Haskell (normal) runtime detected."); - }); - - test('can read file', async () => { - const plugin = await newPlugin('fs.wasm', options => { - options.allowedPaths = { '/mnt': 'tests/data' }; - }); - - const output = await plugin.call("run_test", ""); - const result = decode(output); - - expect(result).toBe("hello world!"); - }); -}); diff --git a/tests/mod.test.ts b/tests/mod.test.ts deleted file mode 100644 index 23abc03..0000000 --- a/tests/mod.test.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { assertEquals, assertRejects } from 'https://deno.land/std@0.200.0/assert/mod.ts'; -import { assertSpyCalls, spy } from 'https://deno.land/std@0.200.0/testing/mock.ts'; -import createPlugin, { CurrentPlugin, ExtismPlugin, ExtismPluginOptions, Manifest, ManifestWasm } from '../src/deno/mod.ts'; - -async function newPlugin( - moduleName: string | Manifest | ManifestWasm, - optionsConfig?: (opts: ExtismPluginOptions) => void, -): Promise { - - const options: ExtismPluginOptions = { - useWasi: true, - runtime: { - url: 'wasm/extism-runtime.wasm', - }, - } - - if (optionsConfig) { - optionsConfig(options); - } - - let module: Manifest | ManifestWasm; - if (typeof moduleName == 'string') { - module = { - url: `wasm/${moduleName}`, - }; - } else { - module = moduleName; - } - - const plugin = await createPlugin(module, options); - return plugin; -} - -function decode(buffer: Uint8Array) { - const decoder = new TextDecoder(); - return decoder.decode(buffer); -} - -Deno.test('can create plugin from string', async () => { - const plugin = await createPlugin("wasm/code.wasm", { - useWasi: true - }); - - assertEquals(await plugin.functionExists('count_vowels'), true); -}); - -Deno.test('can create plugin from url', async () => { - const plugin = await newPlugin({ - url: 'https://raw.githubusercontent.com/extism/extism/main/wasm/code.wasm', - hash: '7def5bb4aa3843a5daf5d6078f1e8540e5ef10b035a9d9387e9bd5156d2b2565', - }); - - assertEquals(await plugin.functionExists('count_vowels'), true); -}); - -Deno.test('fails on hash mismatch', async () => { - await assertRejects( - () => - newPlugin({ - url: 'wasm/code.wasm', - hash: '----', - }), - Error, - 'hash mismatch', - ); -}); - -Deno.test('can use embedded runtime', async () => { - let module = { - url: `wasm/code.wasm`, - }; - - const plugin = await createPlugin(module, { - useWasi: true - }); - - let output = await plugin.call('count_vowels', 'this is a test'); - let result = JSON.parse(decode(output)); - assertEquals(result['count'], 4); -}); - -Deno.test('can create and call a plugin', async () => { - const plugin = await newPlugin('code.wasm'); - let output = await plugin.call('count_vowels', 'this is a test'); - let result = JSON.parse(decode(output)); - assertEquals(result['count'], 4); - - output = await plugin.call('count_vowels', 'this is a test again'); - result = JSON.parse(decode(output)); - assertEquals(result['count'], 7); - - output = await plugin.call('count_vowels', 'this is a test thrice'); - result = JSON.parse(decode(output)); - assertEquals(result['count'], 6); - - output = await plugin.call('count_vowels', '🌎hello🌎world🌎'); - result = JSON.parse(decode(output)); - assertEquals(result['count'], 3); -}); - -Deno.test('can detect if function exists or not', async () => { - const plugin = await newPlugin('code.wasm'); - assertEquals(await plugin.functionExists('count_vowels'), true); - assertEquals(await plugin.functionExists('i_dont_extist'), false); -}); - -Deno.test('can detect if function exists or not', async () => { - const plugin = await newPlugin('code.wasm'); - assertEquals(await plugin.functionExists('count_vowels'), true); - assertEquals(await plugin.functionExists('i_dont_extist'), false); -}); - -Deno.test('errors when function is not known', async () => { - const plugin = await newPlugin('code.wasm'); - await assertRejects(() => plugin.call('i_dont_exist', 'example-input'), Error, 'Plugin error'); -}); - -Deno.test('host functions works', async () => { - const plugin = await newPlugin('code-functions.wasm', (options) => { - options.functions = { - "env": { - "hello_world": function (cp: CurrentPlugin, off: bigint) { - const result = JSON.parse(cp.readString(off) ?? ''); - result['message'] = 'hello from host!'; - return plugin.currentPlugin.writeString(JSON.stringify(result)); - } - } - } - }); - - const output = await plugin.call('count_vowels', 'aaa'); - const result = JSON.parse(decode(output)); - - assertEquals(result, { - count: 3, - message: 'hello from host!', - }); -}); - -Deno.env.set('NO_COLOR', '1'); - -Deno.test('plugin can allocate memory', async () => { - const plugin = await newPlugin('alloc.wasm'); - await plugin.call('run_test', ''); -}); - -Deno.test('plugin can fail gracefully', async () => { - const plugin = await newPlugin('fail.wasm'); - await assertRejects(() => plugin.call('run_test', ''), Error, 'Call error'); -}); - -Deno.test('can deny http requests', async () => { - const plugin = await newPlugin('http.wasm'); - await assertRejects(() => plugin.call('run_test', ''), Error, 'http'); -}); - -Deno.test('can allow http requests', async () => { - const plugin = await newPlugin('http.wasm', (options) => { - options.allowedHosts = ['*.typicode.com']; - }); - - // http is not supported in Deno - await assertRejects(() => plugin.call('run_test', ''), Error, 'http'); -}); - -Deno.test('can log messages', async () => { - const logSpy = spy(console, 'log'); - const warnSpy = spy(console, 'warn'); - const errorSpy = spy(console, 'error'); - const debugSpy = spy(console, 'debug'); - - try { - const plugin = await newPlugin('log.wasm'); - const _ = await plugin.call('run_test', ''); - } finally { - logSpy.restore(); - warnSpy.restore(); - errorSpy.restore(); - debugSpy.restore(); - } - - assertSpyCalls(logSpy, 1); - assertSpyCalls(warnSpy, 1); - assertSpyCalls(errorSpy, 1); - assertSpyCalls(debugSpy, 1); -}); - -Deno.test('can initialize Haskell runtime', async () => { - const plugin = await newPlugin('hello_haskell.wasm', (options) => { - options.config = { 'greeting': 'Howdy' }; - }); - - let output = await plugin.call('testing', 'John'); - let result = decode(output); - assertEquals(result, 'Howdy, John'); - - output = await plugin.call('testing', 'Ben'); - result = decode(output); - assertEquals(result, 'Howdy, Ben'); -}); - -Deno.test('can access fs', async () => { - const plugin = await newPlugin('fs.wasm', (options) => { - options.allowedPaths = { '/mnt': 'tests/data' }; - }); - - const output = await plugin.call('run_test', ''); - const result = decode(output); - assertEquals(result, 'hello world!'); -}); diff --git a/tests/playwright.test.js b/tests/playwright.test.js new file mode 100644 index 0000000..98dd45a --- /dev/null +++ b/tests/playwright.test.js @@ -0,0 +1,23 @@ +import { test, expect } from '@playwright/test'; + +test('tape succeeds', async ({ page }) => { + const finished = new Promise(resolve => { + page.on('console', msg => { + if (/^# (not )?ok/.test(msg.text())) { + resolve() + } + }) + }) + + page.on('console', msg => console.log('>', msg.text())) + page.on('pageerror', err => { + console.error(err); + expect(err).toBeNull(); + }); + + page.on('console', msg => { + expect(msg.text()).not.toMatch(/^not ok/) + }); + await page.goto('http://localhost:8124/dist/tests/browser/'); + await finished +}); diff --git a/tsconfig.browser.json b/tsconfig.browser.json deleted file mode 100644 index ecd8842..0000000 --- a/tsconfig.browser.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "./tsconfig.json", - "files": [ - "./src/browser/index.ts" - ] -} diff --git a/tsconfig.json b/tsconfig.json index 8fe47fb..0dfa102 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,13 +1,16 @@ { "compilerOptions": { - "target": "es2022", - "module": "commonjs", + "target": "es2022", + "module": "commonjs", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, + "allowImportingTsExtensions": true, "declaration": true, - "strict": true, + "strict": true, "skipLibCheck": true, - "allowJs": true + "allowJs": true, + "typeRoots": ["./types", "node_modules/@types"] }, - "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*mod.ts"] + "exclude": ["node_modules", "dist"], + "files": ["./src/mod.ts"] } diff --git a/tsconfig.node.json b/tsconfig.node.json deleted file mode 100644 index 95ba222..0000000 --- a/tsconfig.node.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "./tsconfig.json", - "files": [ - "./src/node/index.ts" - ] -} diff --git a/tslint.json b/tslint.json deleted file mode 100644 index 71287e5..0000000 --- a/tslint.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": [ - "tslint:recommended", - "tslint-config-prettier" - ] -} \ No newline at end of file diff --git a/types/js-sdk:features/index.d.ts b/types/js-sdk:features/index.d.ts new file mode 100644 index 0000000..d6ba716 --- /dev/null +++ b/types/js-sdk:features/index.d.ts @@ -0,0 +1,3 @@ +declare module 'js-sdk:features' { + export const FEATURES: Record; +} diff --git a/types/js-sdk:fs/index.d.ts b/types/js-sdk:fs/index.d.ts new file mode 100644 index 0000000..fc3b9f3 --- /dev/null +++ b/types/js-sdk:fs/index.d.ts @@ -0,0 +1,3 @@ +declare module 'js-sdk:fs' { + function readFile(path: string): Promise; +} diff --git a/types/js-sdk:wasi/index.d.ts b/types/js-sdk:wasi/index.d.ts new file mode 100644 index 0000000..0f7be63 --- /dev/null +++ b/types/js-sdk:wasi/index.d.ts @@ -0,0 +1,7 @@ +declare module 'js-sdk:wasi' { + interface InternalWasi { + importObject(): Promise>; + initialize(instance: WebAssembly.Instance): Promise + } + function loadWasi(allowedPaths: {[from: string]: string}): Promise; +} diff --git a/types/js-sdk:worker-url/index.d.ts b/types/js-sdk:worker-url/index.d.ts new file mode 100644 index 0000000..1157259 --- /dev/null +++ b/types/js-sdk:worker-url/index.d.ts @@ -0,0 +1,3 @@ +declare module 'js-sdk:worker-url' { + declare const WORKER_URL: URL; +} diff --git a/update-kernel.js b/update-kernel.js deleted file mode 100644 index 9b6561d..0000000 --- a/update-kernel.js +++ /dev/null @@ -1,27 +0,0 @@ -const fs = require("fs"); -const crypto = require("crypto"); - -async function main() { - const pluginPath = "src/plugin.ts"; - let pluginContents = await fs.promises.readFile(pluginPath, "utf8"); - - const kernelPath = "wasm/extism-runtime.wasm"; - const kernelContents = await fs.promises.readFile(kernelPath); - const kernelBase64 = kernelContents.toString("base64"); - const kernelHash = await crypto.createHash("sha256").update(kernelContents) - .digest("hex"); - console.log(kernelHash); - - pluginContents = pluginContents.replace( - /embeddedRuntime = '.*'/, - `embeddedRuntime = '${kernelBase64}'`, - ); - pluginContents = pluginContents.replace( - /embeddedRuntimeHash = '.*'/, - `embeddedRuntimeHash = '${kernelHash}'`, - ); - - await fs.promises.writeFile(pluginPath, pluginContents); -} - -main(); From e14d0f8ce5bda2f03dc665fef6a11ef205f235f7 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Thu, 26 Oct 2023 14:16:23 -0700 Subject: [PATCH 02/25] fix(build): ubuntu base64 requires flag for single-line output --- justfile | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/justfile b/justfile index 0d11ce9..78556a3 100644 --- a/justfile +++ b/justfile @@ -101,8 +101,15 @@ build_worker out args='[]': }] + . ')" just _build {{ out }} "$config" + + if [ $(uname) == 'Darwin' ]; then + flag="-b" + else + flag="-w" + fi + echo "export const WORKER_URL = new URL($( - dist/{{ out }}/worker-url.ts From a86b31b7e45a139dd057744bf853e4682bff6c52 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Thu, 26 Oct 2023 16:49:06 -0700 Subject: [PATCH 03/25] feat: add bun support --- justfile | 41 ++++++++++++++++++++++++++++++- package.json | 1 + src/mod.test.ts | 2 +- src/polyfills/browser:features.ts | 2 ++ src/polyfills/bun:features.ts | 18 ++++++++++++++ src/polyfills/bun:worker-url.ts | 1 + src/polyfills/deno:features.ts | 2 ++ src/polyfills/node:features.ts | 2 ++ 8 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 src/polyfills/bun:features.ts create mode 100644 src/polyfills/bun:worker-url.ts diff --git a/justfile b/justfile index 78556a3..a39c87f 100644 --- a/justfile +++ b/justfile @@ -190,6 +190,25 @@ build_node_esm out='esm' args='[]': just _build {{ out }} "$config" echo '{"type":"module"}' > dist/{{ out }}/package.json +build_bun out='bun' args='[]': + #!/bin/bash + config="$(<<<'{{ args }}' jq -cM ' + [{ + "entryPoints": ["src/mod.ts", "src/worker.ts"], + "platform": "node", + "format": "esm", + "minify": false, + "alias": { + "js-sdk:worker-url": "./src/polyfills/bun:worker-url.ts", + "js-sdk:features": "./src/polyfills/bun:features.ts", + "js-sdk:fs": "node:fs/promises", + "js-sdk:wasi": "./src/polyfills/node:wasi.ts", + } + }] + . + ')" + just _build {{ out }} "$config" + echo '{"type":"module"}' > dist/{{ out }}/package.json + build_browser out='browser' args='[]': #!/bin/bash config="$(<<<'{{ args }}' jq -cM ' @@ -214,6 +233,9 @@ _build_node_tests: just build_node_cjs 'tests/cjs' '[{"minify": false, "entryPoints":["src/mod.test.ts"]}]' just build_node_esm 'tests/esm' '[{"minify": false, "entryPoints":["src/mod.test.ts"]}]' +_build_bun_tests: + just build_bun 'tests/bun' '[{"minify": false, "entryPoints":["src/mod.test.ts"], "alias": {"node:test": "tape"}}]' + _build_browser_tests out='tests/browser' args='[]': #!/bin/bash config="$(<<<'{{ args }}' jq -cM ' @@ -232,7 +254,7 @@ _build_browser_tests out='tests/browser' args='[]': cp dist/{{ out }}/mod.test.js dist/{{ out }}/$hashed.js echo '' > dist/{{ out }}/index.html -build: prepare build_worker_node build_worker_browser build_browser build_node_esm build_node_cjs _build_browser_tests _build_node_tests +build: prepare build_worker_node build_worker_browser build_browser build_node_esm build_node_cjs build_bun _build_browser_tests _build_node_tests _build_bun_tests npm pack --pack-destination dist/ test: build && test-artifacts @@ -249,6 +271,7 @@ test: build && test-artifacts deno test -A src/mod.test.ts node --no-warnings --test dist/tests/cjs/*.test.js node --no-warnings --test dist/tests/esm/*.test.js + bun run dist/tests/bun/*.test.js playwright test --browser all tests/playwright.test.js test-artifacts: @@ -296,6 +319,22 @@ test-artifacts: } EOF + cat >./index.js < { } }); -if (FEATURES.fsAccess) { +if (FEATURES.fsAccess && FEATURES.supportsWasiPreview1) { test('can access fs', async () => { const plugin = await createPlugin('http://localhost:8124/wasm/fs.wasm', { allowedPaths: { '/mnt': 'tests/data' }, diff --git a/src/polyfills/browser:features.ts b/src/polyfills/browser:features.ts index 97512e9..594763a 100644 --- a/src/polyfills/browser:features.ts +++ b/src/polyfills/browser:features.ts @@ -16,4 +16,6 @@ export const FEATURES = { ? (window as any).crossOriginIsolated && typeof SharedArrayBuffer !== 'undefined' : true ), + + supportsWasiPreview1: true, } diff --git a/src/polyfills/bun:features.ts b/src/polyfills/bun:features.ts new file mode 100644 index 0000000..2d5b3da --- /dev/null +++ b/src/polyfills/bun:features.ts @@ -0,0 +1,18 @@ +export const FEATURES = { + // When false, shared buffers have to be copied to an array + // buffer before passing to Text{En,De}coding() + allowSharedBufferCodec: false, + + // Whether or not the manifest supports the "path:" key. + manifestSupportsPaths: true, + + // Whether or not cross-origin checks are enforced on this platform. + crossOriginChecksEnforced: false, + + fsAccess: true, + + hasOffThreadCapability: true, + + // See https://github.com/oven-sh/bun/issues/1960 + supportsWasiPreview1: false, +} diff --git a/src/polyfills/bun:worker-url.ts b/src/polyfills/bun:worker-url.ts new file mode 100644 index 0000000..341752a --- /dev/null +++ b/src/polyfills/bun:worker-url.ts @@ -0,0 +1 @@ +export const WORKER_URL = new URL("./worker.js", import.meta.url); diff --git a/src/polyfills/deno:features.ts b/src/polyfills/deno:features.ts index 7568f4e..0587476 100644 --- a/src/polyfills/deno:features.ts +++ b/src/polyfills/deno:features.ts @@ -12,4 +12,6 @@ export const FEATURES = { fsAccess: true, hasOffThreadCapability: true, + + supportsWasiPreview1: true, } diff --git a/src/polyfills/node:features.ts b/src/polyfills/node:features.ts index 22b9f87..47c1947 100644 --- a/src/polyfills/node:features.ts +++ b/src/polyfills/node:features.ts @@ -12,4 +12,6 @@ export const FEATURES = { fsAccess: true, hasOffThreadCapability: true, + + supportsWasiPreview1: true, } From a3c53548e59e51d96193c75a790fc2b0a1d31ad9 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Thu, 26 Oct 2023 17:15:12 -0700 Subject: [PATCH 04/25] fix: remove paths with ":"; windows does not support them --- .github/workflows/ci.yml | 19 +++++------ DEVELOPING.md | 16 ++++----- deno.json | 6 ++-- justfile | 34 +++++++++---------- ...rowser:features.ts => browser-features.ts} | 0 .../{browser:fs.ts => browser-fs.ts} | 0 .../{browser:wasi.ts => browser-wasi.ts} | 0 .../{bun:features.ts => bun-features.ts} | 0 .../{bun:worker-url.ts => bun-worker-url.ts} | 0 .../{deno:features.ts => deno-features.ts} | 0 src/polyfills/{deno:wasi.ts => deno-wasi.ts} | 0 ...threads.ts => host-node-worker_threads.ts} | 0 .../{node:features.ts => node-features.ts} | 0 src/polyfills/{node:fs.ts => node-fs.ts} | 0 src/polyfills/{node:wasi.ts => node-wasi.ts} | 0 ...reads.ts => worker-node-worker_threads.ts} | 0 types/{js-sdk:wasi => js-sdk}/index.d.ts | 9 +++++ types/js-sdk:features/index.d.ts | 3 -- types/js-sdk:fs/index.d.ts | 3 -- types/js-sdk:worker-url/index.d.ts | 3 -- 20 files changed, 46 insertions(+), 47 deletions(-) rename src/polyfills/{browser:features.ts => browser-features.ts} (100%) rename src/polyfills/{browser:fs.ts => browser-fs.ts} (100%) rename src/polyfills/{browser:wasi.ts => browser-wasi.ts} (100%) rename src/polyfills/{bun:features.ts => bun-features.ts} (100%) rename src/polyfills/{bun:worker-url.ts => bun-worker-url.ts} (100%) rename src/polyfills/{deno:features.ts => deno-features.ts} (100%) rename src/polyfills/{deno:wasi.ts => deno-wasi.ts} (100%) rename src/polyfills/{host:node:worker_threads.ts => host-node-worker_threads.ts} (100%) rename src/polyfills/{node:features.ts => node-features.ts} (100%) rename src/polyfills/{node:fs.ts => node-fs.ts} (100%) rename src/polyfills/{node:wasi.ts => node-wasi.ts} (100%) rename src/polyfills/{worker:node:worker_threads.ts => worker-node-worker_threads.ts} (100%) rename types/{js-sdk:wasi => js-sdk}/index.d.ts (54%) delete mode 100644 types/js-sdk:features/index.d.ts delete mode 100644 types/js-sdk:fs/index.d.ts delete mode 100644 types/js-sdk:worker-url/index.d.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9250b4b..b50c7ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,20 +19,19 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Setup Node.js environment - uses: actions/setup-node@v3.8.1 - - - name: Setup Deno - uses: denoland/setup-deno@v1.1.2 - + - uses: actions/setup-node@v3.8.1 + + - uses: denoland/setup-deno@v1.1.2 + + - uses: oven-sh/setup-bun@v1 + with: + bun-version: latest + - name: NPM install run: npm ci - name: Build run: npm run build - - name: Node Test + - name: Test run: npm run test - - - name: Deno Test - run: deno test -A ./tests/mod.test.ts diff --git a/DEVELOPING.md b/DEVELOPING.md index f293449..12dfce8 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -32,10 +32,10 @@ build_worker_browser out='worker/browser' args='[]': # <-- we accept args and an [{ "format": "esm", "alias": { - "js-sdk:features": "./src/polyfills/browser:features.ts", - "node:worker_threads": "./src/polyfills/worker:node:worker_threads.ts", - "js-sdk:fs": "./src/polyfills/browser:fs.ts", - "js-sdk:wasi": "./src/polyfills/browser:wasi.ts", + "js-sdk:features": "./src/polyfills/browser-features.ts", + "node:worker_threads": "./src/polyfills/worker-node-worker_threads.ts", + "js-sdk:fs": "./src/polyfills/browser-fs.ts", + "js-sdk:wasi": "./src/polyfills/browser-wasi.ts", } }] + . # <--- add this recipe's flags to the incoming flags. ')" @@ -54,12 +54,12 @@ differences at module boundaries and replace them as-needed. For example: each of Node, Deno, and the Browser have different WASI libraries with slightly different interfaces. We define a **virtual module**, `js-sdk:wasi`, and implement it by: -1. Modifying `deno.json`; adding a mapping from `js-sdk:wasi` to `./src/polyfills/deno:wasi.ts`. +1. Modifying `deno.json`; adding a mapping from `js-sdk:wasi` to `./src/polyfills/deno-wasi.ts`. 2. Adding a `types/js-sdk:wasi/index.d.ts` file. 3. Modifying the esbuild `alias` added by `build_worker`, `build_worker_node`, `build_node_cjs`, `build_node_esm`, and `build_browser`. - - Node overrides are set to `./src/polyfills/node:wasi.ts`. - - Browser overrides are set to `./src/polyfills/browser:wasi.ts`. + - Node overrides are set to `./src/polyfills/node-wasi.ts`. + - Browser overrides are set to `./src/polyfills/browser-wasi.ts`. In this manner, differences between the platforms are hidden and the core of the library can be written in "mutually intelligble" TypeScript. @@ -67,7 +67,7 @@ the library can be written in "mutually intelligble" TypeScript. One notable exception to this rule: Deno implements Node polyfills; for complicated imports, like `node:worker_threads`, we instead only polyfill the browser. The browser polyfill is split into `host:node:worker_threads.ts` and -`worker:node:worker_threads.ts`: these polyfill just enough of the Node worker +`worker-node-worker_threads.ts`: these polyfill just enough of the Node worker thread API over the top of builtin workers to make them adhere to the same interface. diff --git a/deno.json b/deno.json index 7be6989..bd5a128 100644 --- a/deno.json +++ b/deno.json @@ -1,8 +1,8 @@ { "imports": { "js-sdk:worker-url": "./src/worker-url.ts", - "js-sdk:features": "./src/polyfills/deno:features.ts", - "js-sdk:wasi": "./src/polyfills/deno:wasi.ts", - "js-sdk:fs": "./src/polyfills/node:fs.ts" + "js-sdk:features": "./src/polyfills/deno-features.ts", + "js-sdk:wasi": "./src/polyfills/deno-wasi.ts", + "js-sdk:fs": "./src/polyfills/node-fs.ts" } } diff --git a/justfile b/justfile index a39c87f..11a7738 100644 --- a/justfile +++ b/justfile @@ -119,9 +119,9 @@ build_worker_node out='worker/node' args='[]': [{ "platform": "node", "alias": { - "js-sdk:features": "./src/polyfills/node:features.ts", + "js-sdk:features": "./src/polyfills/node-features.ts", "js-sdk:fs": "node:fs", - "js-sdk:wasi": "./src/polyfills/node:wasi.ts", + "js-sdk:wasi": "./src/polyfills/node-wasi.ts", } }] + . ')" @@ -133,10 +133,10 @@ build_worker_browser out='worker/browser' args='[]': [{ "format": "esm", "alias": { - "js-sdk:features": "./src/polyfills/browser:features.ts", - "node:worker_threads": "./src/polyfills/worker:node:worker_threads.ts", - "js-sdk:fs": "./src/polyfills/browser:fs.ts", - "js-sdk:wasi": "./src/polyfills/browser:wasi.ts", + "js-sdk:features": "./src/polyfills/browser-features.ts", + "node:worker_threads": "./src/polyfills/worker-node-worker_threads.ts", + "js-sdk:fs": "./src/polyfills/browser-fs.ts", + "js-sdk:wasi": "./src/polyfills/browser-wasi.ts", } }] + . ')" @@ -150,10 +150,10 @@ build_node_cjs out='cjs' args='[]': "platform": "node", "minify": false, "alias": { - "js-sdk:features": "./src/polyfills/node:features.ts", + "js-sdk:features": "./src/polyfills/node-features.ts", "js-sdk:worker-url": "./dist/worker/node/worker-url.ts", "js-sdk:fs": "node:fs/promises", - "js-sdk:wasi": "./src/polyfills/node:wasi.ts", + "js-sdk:wasi": "./src/polyfills/node-wasi.ts", }, "define": { "import.meta.url": "__filename" @@ -180,10 +180,10 @@ build_node_esm out='esm' args='[]': "format": "esm", "minify": false, "alias": { - "js-sdk:features": "./src/polyfills/node:features.ts", + "js-sdk:features": "./src/polyfills/node-features.ts", "js-sdk:worker-url": "./dist/worker/node/worker-url.ts", "js-sdk:fs": "node:fs/promises", - "js-sdk:wasi": "./src/polyfills/node:wasi.ts", + "js-sdk:wasi": "./src/polyfills/node-wasi.ts", } }] + . ')" @@ -199,10 +199,10 @@ build_bun out='bun' args='[]': "format": "esm", "minify": false, "alias": { - "js-sdk:worker-url": "./src/polyfills/bun:worker-url.ts", - "js-sdk:features": "./src/polyfills/bun:features.ts", + "js-sdk:worker-url": "./src/polyfills/bun-worker-url.ts", + "js-sdk:features": "./src/polyfills/bun-features.ts", "js-sdk:fs": "node:fs/promises", - "js-sdk:wasi": "./src/polyfills/node:wasi.ts", + "js-sdk:wasi": "./src/polyfills/node-wasi.ts", } }] + . ')" @@ -218,11 +218,11 @@ build_browser out='browser' args='[]': "define": {"global": "window"}, "format": "esm", "alias": { - "js-sdk:features": "./src/polyfills/browser:features.ts", - "node:worker_threads": "./src/polyfills/host:node:worker_threads.ts", - "js-sdk:fs": "./src/polyfills/browser:fs.ts", + "js-sdk:features": "./src/polyfills/browser-features.ts", + "node:worker_threads": "./src/polyfills/host-node-worker_threads.ts", + "js-sdk:fs": "./src/polyfills/browser-fs.ts", "js-sdk:worker-url": "./dist/worker/browser/worker-url.ts", - "js-sdk:wasi": "./src/polyfills/browser:wasi.ts", + "js-sdk:wasi": "./src/polyfills/browser-wasi.ts", } }] + . ')" diff --git a/src/polyfills/browser:features.ts b/src/polyfills/browser-features.ts similarity index 100% rename from src/polyfills/browser:features.ts rename to src/polyfills/browser-features.ts diff --git a/src/polyfills/browser:fs.ts b/src/polyfills/browser-fs.ts similarity index 100% rename from src/polyfills/browser:fs.ts rename to src/polyfills/browser-fs.ts diff --git a/src/polyfills/browser:wasi.ts b/src/polyfills/browser-wasi.ts similarity index 100% rename from src/polyfills/browser:wasi.ts rename to src/polyfills/browser-wasi.ts diff --git a/src/polyfills/bun:features.ts b/src/polyfills/bun-features.ts similarity index 100% rename from src/polyfills/bun:features.ts rename to src/polyfills/bun-features.ts diff --git a/src/polyfills/bun:worker-url.ts b/src/polyfills/bun-worker-url.ts similarity index 100% rename from src/polyfills/bun:worker-url.ts rename to src/polyfills/bun-worker-url.ts diff --git a/src/polyfills/deno:features.ts b/src/polyfills/deno-features.ts similarity index 100% rename from src/polyfills/deno:features.ts rename to src/polyfills/deno-features.ts diff --git a/src/polyfills/deno:wasi.ts b/src/polyfills/deno-wasi.ts similarity index 100% rename from src/polyfills/deno:wasi.ts rename to src/polyfills/deno-wasi.ts diff --git a/src/polyfills/host:node:worker_threads.ts b/src/polyfills/host-node-worker_threads.ts similarity index 100% rename from src/polyfills/host:node:worker_threads.ts rename to src/polyfills/host-node-worker_threads.ts diff --git a/src/polyfills/node:features.ts b/src/polyfills/node-features.ts similarity index 100% rename from src/polyfills/node:features.ts rename to src/polyfills/node-features.ts diff --git a/src/polyfills/node:fs.ts b/src/polyfills/node-fs.ts similarity index 100% rename from src/polyfills/node:fs.ts rename to src/polyfills/node-fs.ts diff --git a/src/polyfills/node:wasi.ts b/src/polyfills/node-wasi.ts similarity index 100% rename from src/polyfills/node:wasi.ts rename to src/polyfills/node-wasi.ts diff --git a/src/polyfills/worker:node:worker_threads.ts b/src/polyfills/worker-node-worker_threads.ts similarity index 100% rename from src/polyfills/worker:node:worker_threads.ts rename to src/polyfills/worker-node-worker_threads.ts diff --git a/types/js-sdk:wasi/index.d.ts b/types/js-sdk/index.d.ts similarity index 54% rename from types/js-sdk:wasi/index.d.ts rename to types/js-sdk/index.d.ts index 0f7be63..fee463f 100644 --- a/types/js-sdk:wasi/index.d.ts +++ b/types/js-sdk/index.d.ts @@ -1,3 +1,9 @@ +declare module 'js-sdk:features' { + export const FEATURES: Record; +} +declare module 'js-sdk:fs' { + function readFile(path: string): Promise; +} declare module 'js-sdk:wasi' { interface InternalWasi { importObject(): Promise>; @@ -5,3 +11,6 @@ declare module 'js-sdk:wasi' { } function loadWasi(allowedPaths: {[from: string]: string}): Promise; } +declare module 'js-sdk:worker-url' { + declare const WORKER_URL: URL; +} diff --git a/types/js-sdk:features/index.d.ts b/types/js-sdk:features/index.d.ts deleted file mode 100644 index d6ba716..0000000 --- a/types/js-sdk:features/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare module 'js-sdk:features' { - export const FEATURES: Record; -} diff --git a/types/js-sdk:fs/index.d.ts b/types/js-sdk:fs/index.d.ts deleted file mode 100644 index fc3b9f3..0000000 --- a/types/js-sdk:fs/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare module 'js-sdk:fs' { - function readFile(path: string): Promise; -} diff --git a/types/js-sdk:worker-url/index.d.ts b/types/js-sdk:worker-url/index.d.ts deleted file mode 100644 index 1157259..0000000 --- a/types/js-sdk:worker-url/index.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare module 'js-sdk:worker-url' { - declare const WORKER_URL: URL; -} From 6a075903dc3a45bb26cf1e73e4babd52b315e266 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Thu, 26 Oct 2023 17:26:40 -0700 Subject: [PATCH 05/25] fix(build): do not install bun on windows --- .github/workflows/ci.yml | 1 + justfile | 22 +++++----------------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b50c7ca..b733a49 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,7 @@ jobs: - uses: denoland/setup-deno@v1.1.2 - uses: oven-sh/setup-bun@v1 + if: ${{ matrix.os != 'windows-latest' }} with: bun-version: latest diff --git a/justfile b/justfile index 11a7738..67bb443 100644 --- a/justfile +++ b/justfile @@ -28,7 +28,7 @@ prepare: npm ci fi - playwright install + playwright install --with-deps _build out args='[]': prepare #!/bin/bash @@ -271,7 +271,7 @@ test: build && test-artifacts deno test -A src/mod.test.ts node --no-warnings --test dist/tests/cjs/*.test.js node --no-warnings --test dist/tests/esm/*.test.js - bun run dist/tests/bun/*.test.js + if &>/dev/null which bun; then bun run dist/tests/bun/*.test.js; fi playwright test --browser all tests/playwright.test.js test-artifacts: @@ -304,21 +304,6 @@ test-artifacts: }) EOF - node --input-type=module --no-warnings <./index.js </dev/null which bun; then bun run index.js; fi bun run index.js lint *args: From ed07db72c4e975691d5e652c07f945cd31220658 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Thu, 26 Oct 2023 17:30:03 -0700 Subject: [PATCH 06/25] fix(build): just-install npm pkg does not work on windows; use gh action --- .github/workflows/ci.yml | 2 ++ package-lock.json | 14 -------------- package.json | 1 - 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b733a49..d8d95d8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,8 @@ jobs: steps: - uses: actions/checkout@v3 + - uses: extractions/setup-just@v1 + - uses: actions/setup-node@v3.8.1 - uses: denoland/setup-deno@v1.1.2 diff --git a/package-lock.json b/package-lock.json index 3cad289..e5f121f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,6 @@ "esbuild": "^0.15.13", "esbuild-node-builtin": "^0.1.1", "eslint": "^8.51.0", - "just-install": "^1.0.11", "playwright": "^1.39.0", "prettier": "^2.7.1", "tape": "^5.7.1", @@ -2646,19 +2645,6 @@ "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, - "node_modules/just-install": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/just-install/-/just-install-1.0.11.tgz", - "integrity": "sha512-3nW53hWK9muHUTXthiTcUqFkd5FXDad7YLoDWJonVhPUZxX8PgCVGvaI8AQ5gqJZ45MBd/hdqg5XPIWPh9Bk/g==", - "dev": true, - "hasInstallScript": true, - "bin": { - "just": "bin/just" - }, - "engines": { - "node": ">=8.5" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", diff --git a/package.json b/package.json index 563c8c6..96d970e 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,6 @@ "esbuild": "^0.15.13", "esbuild-node-builtin": "^0.1.1", "eslint": "^8.51.0", - "just-install": "^1.0.11", "playwright": "^1.39.0", "prettier": "^2.7.1", "tape": "^5.7.1", From 23d2ff63de53fb24d638d3d14c2282320ff9327f Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Thu, 26 Oct 2023 17:36:09 -0700 Subject: [PATCH 07/25] fix(build): use lts node version (>=20) --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d8d95d8..b656b72 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,8 @@ jobs: - uses: extractions/setup-just@v1 - uses: actions/setup-node@v3.8.1 + with: + node-version: lts - uses: denoland/setup-deno@v1.1.2 From e4b9e8dc8522058e7872f578c50d107c464a3e86 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Thu, 26 Oct 2023 17:37:16 -0700 Subject: [PATCH 08/25] fix(ci): remove install/build steps --- .github/workflows/ci.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b656b72..4c17809 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,8 @@ jobs: - uses: actions/setup-node@v3.8.1 with: - node-version: lts + node-version: lts/* + check-latest: true - uses: denoland/setup-deno@v1.1.2 @@ -32,11 +33,5 @@ jobs: with: bun-version: latest - - name: NPM install - run: npm ci - - - name: Build - run: npm run build - - name: Test run: npm run test From fd003862b6d2c67eb3d8d61db79f44c4f72cc51b Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Thu, 26 Oct 2023 20:53:36 -0700 Subject: [PATCH 09/25] fix(build): remove shasum call from build --- justfile | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/justfile b/justfile index 67bb443..75346f1 100644 --- a/justfile +++ b/justfile @@ -249,10 +249,7 @@ _build_browser_tests out='tests/browser' args='[]': ')" just build_browser {{ out }} "$config" echo '{"type":"module"}' > dist/{{ out }}/package.json - - hashed=$(shasum dist/{{ out }}/mod.test.js | awk '{print $1}') - cp dist/{{ out }}/mod.test.js dist/{{ out }}/$hashed.js - echo '' > dist/{{ out }}/index.html + echo '' > dist/{{ out }}/index.html build: prepare build_worker_node build_worker_browser build_browser build_node_esm build_node_cjs build_bun _build_browser_tests _build_node_tests _build_bun_tests npm pack --pack-destination dist/ From 1a370d53ae01e437f9d67e1b50fd7cd4c880c926 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Fri, 27 Oct 2023 13:47:22 -0700 Subject: [PATCH 10/25] rename "offMainThread" -> "runInWorker" --- src/mod.test.ts | 6 +++--- src/mod.ts | 8 ++++---- src/plugin-options.ts | 6 +++--- src/polyfills/browser-features.ts | 2 +- src/polyfills/bun-features.ts | 2 +- src/polyfills/deno-features.ts | 2 +- src/polyfills/node-features.ts | 2 +- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/mod.test.ts b/src/mod.test.ts index 82d9885..6feafd0 100644 --- a/src/mod.test.ts +++ b/src/mod.test.ts @@ -185,7 +185,7 @@ test('host functions reject original promise when throwing', async () => { } }) -if (FEATURES.hasOffThreadCapability) { +if (FEATURES.hasWorkerCapability) { test('host functions may be async if worker is off-main-thread', async () => { const functions = { env: { @@ -198,7 +198,7 @@ if (FEATURES.hasOffThreadCapability) { const plugin = await createPlugin({wasm: [ { url: 'http://localhost:8124/wasm/code-functions.wasm' } - ]}, { useWasi: true, functions, offMainThread: true }); + ]}, { useWasi: true, functions, runInWorker: true }); try { const output = await plugin.call('count_vowels', 'hello world'); @@ -220,7 +220,7 @@ if (FEATURES.hasOffThreadCapability) { const plugin = await createPlugin({wasm: [ { url: 'http://localhost:8124/wasm/code-functions.wasm' } - ]}, { useWasi: true, functions, offMainThread: true }); + ]}, { useWasi: true, functions, runInWorker: true }); try { const [err, data] = await plugin.call('count_vowels', 'hello world').then( diff --git a/src/mod.ts b/src/mod.ts index d1a2d3b..0dbce45 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -31,7 +31,7 @@ export type PluginConfig = { [key: string]: string }; */ export interface ExtismPluginOptions { useWasi?: boolean | undefined; - offMainThread?: boolean | undefined; + runInWorker?: boolean | undefined; logger?: Console; functions?: { [key: string]: { [key: string]: any } } | undefined; allowedPaths?: { [key: string]: string } | undefined; @@ -93,8 +93,8 @@ export default async function createPlugin( opts.logger ??= console; opts.config ??= {} - opts.offMainThread ??= FEATURES.hasOffThreadCapability; - if (opts.offMainThread && !FEATURES.hasOffThreadCapability) { + opts.runInWorker ??= FEATURES.hasWorkerCapability; + if (opts.runInWorker && !FEATURES.hasWorkerCapability) { throw new Error( 'Cannot enable off-thread wasm; current context is not `crossOriginIsolated` (see https://mdn.io/crossOriginIsolated)' ); @@ -120,7 +120,7 @@ export default async function createPlugin( config, } - return (opts.offMainThread ? _createBackgroundPlugin : _createForegroundPlugin)( + return (opts.runInWorker ? _createBackgroundPlugin : _createForegroundPlugin)( ic, names, moduleData, diff --git a/src/plugin-options.ts b/src/plugin-options.ts index b6677a8..adfe537 100644 --- a/src/plugin-options.ts +++ b/src/plugin-options.ts @@ -8,7 +8,7 @@ import { FEATURES } from 'js-sdk:features'; export interface ExtismPluginOptions { useWasi?: boolean | undefined; runtime?: ManifestWasm | undefined; - offMainThread?: boolean | undefined; + runInWorker?: boolean | undefined; logger?: Console; functions?: { [key: string]: { [key: string]: any } } | undefined; allowedPaths?: { [key: string]: string } | undefined; @@ -29,8 +29,8 @@ export function intoPluginOptions(opts: ExtismPluginOptions): ExtismPluginOption opts.allowedHosts ??= [].concat(opts.allowedHosts || []); opts.logger ??= console; - opts.offMainThread ??= FEATURES.hasOffThreadCapability; - if (opts.offMainThread && !FEATURES.hasOffThreadCapability) { + opts.runInWorker ??= FEATURES.hasWorkerCapability; + if (opts.runInWorker && !FEATURES.hasWorkerCapability) { throw new Error( 'Cannot enable off-thread wasm; current context is not `crossOriginIsolated` (see https://mdn.io/crossOriginIsolated)' ); diff --git a/src/polyfills/browser-features.ts b/src/polyfills/browser-features.ts index 594763a..b06b3b9 100644 --- a/src/polyfills/browser-features.ts +++ b/src/polyfills/browser-features.ts @@ -11,7 +11,7 @@ export const FEATURES = { fsAccess: false, - hasOffThreadCapability: ( + hasWorkerCapability: ( typeof window !== 'undefined' ? (window as any).crossOriginIsolated && typeof SharedArrayBuffer !== 'undefined' : true diff --git a/src/polyfills/bun-features.ts b/src/polyfills/bun-features.ts index 2d5b3da..5a6b1a1 100644 --- a/src/polyfills/bun-features.ts +++ b/src/polyfills/bun-features.ts @@ -11,7 +11,7 @@ export const FEATURES = { fsAccess: true, - hasOffThreadCapability: true, + hasWorkerCapability: true, // See https://github.com/oven-sh/bun/issues/1960 supportsWasiPreview1: false, diff --git a/src/polyfills/deno-features.ts b/src/polyfills/deno-features.ts index 0587476..1e39a69 100644 --- a/src/polyfills/deno-features.ts +++ b/src/polyfills/deno-features.ts @@ -11,7 +11,7 @@ export const FEATURES = { fsAccess: true, - hasOffThreadCapability: true, + hasWorkerCapability: true, supportsWasiPreview1: true, } diff --git a/src/polyfills/node-features.ts b/src/polyfills/node-features.ts index 47c1947..3e6ab47 100644 --- a/src/polyfills/node-features.ts +++ b/src/polyfills/node-features.ts @@ -11,7 +11,7 @@ export const FEATURES = { fsAccess: true, - hasOffThreadCapability: true, + hasWorkerCapability: true, supportsWasiPreview1: true, } From fe5ade3a961d1b6f738784a3b1bcccb5ee48f9f8 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Fri, 27 Oct 2023 14:08:12 -0700 Subject: [PATCH 11/25] fix: add formatting, lint examples, skip tests if WebAssembly is not defined --- examples/deno.ts | 20 +- examples/index.html | 106 ++-- examples/node.js | 29 +- justfile | 12 + src/background-plugin.ts | 265 +++++----- src/call-context.ts | 197 +++---- src/foreground-plugin.ts | 162 +++--- src/manifest.ts | 110 ++-- src/mod.test.ts | 541 ++++++++++---------- src/mod.ts | 46 +- src/plugin-options.ts | 13 +- src/polyfills/browser-features.ts | 9 +- src/polyfills/browser-fs.ts | 2 +- src/polyfills/browser-wasi.ts | 10 +- src/polyfills/bun-features.ts | 2 +- src/polyfills/bun-worker-url.ts | 2 +- src/polyfills/deno-features.ts | 2 +- src/polyfills/deno-wasi.ts | 10 +- src/polyfills/host-node-worker_threads.ts | 4 +- src/polyfills/node-features.ts | 2 +- src/polyfills/node-wasi.ts | 10 +- src/polyfills/worker-node-worker_threads.ts | 10 +- src/runtime.ts | 3 - src/worker-url.ts | 2 +- src/worker.ts | 250 ++++----- 25 files changed, 932 insertions(+), 887 deletions(-) delete mode 100644 src/runtime.ts diff --git a/examples/deno.ts b/examples/deno.ts index 88b6c64..3285199 100755 --- a/examples/deno.ts +++ b/examples/deno.ts @@ -1,19 +1,19 @@ #!/usr/bin/env deno run -A -import createPlugin from '../src/mod.ts' +import createPlugin from '../src/mod.ts'; -const filename = Deno.args[0] || "wasm/hello.wasm"; -const funcname = Deno.args[1] || "run_test"; -const input = Deno.args[2] || "this is a test"; +const filename = Deno.args[0] || 'wasm/hello.wasm'; +const funcname = Deno.args[1] || 'run_test'; +const input = Deno.args[2] || 'this is a test'; const plugin = await createPlugin(filename, { - useWasi: true, - config: { - "thing": "testing" - } + useWasi: true, + config: { + thing: 'testing', + }, }); const res = await plugin.call(funcname, new TextEncoder().encode(input)); const s = new TextDecoder().decode(res.buffer); -console.log(s) +console.log(s); -await plugin.close() +await plugin.close(); diff --git a/examples/index.html b/examples/index.html index 9057bfd..73518ca 100644 --- a/examples/index.html +++ b/examples/index.html @@ -1,43 +1,41 @@ - - - - + + + WASM Plugin in Browser - - - + + @@ -56,31 +54,29 @@ - - - + diff --git a/examples/node.js b/examples/node.js index ac2d14d..0694685 100755 --- a/examples/node.js +++ b/examples/node.js @@ -1,23 +1,24 @@ #!/usr/bin/env node --no-warnings -const createPlugin = require("../dist/cjs").default; -const { argv } = require("process"); +/* eslint-disable @typescript-eslint/no-var-requires */ +const createPlugin = require('../dist/cjs').default; +const { argv } = require('process'); async function main() { - const filename = argv[2] || "wasm/hello.wasm"; - const funcname = argv[3] || "run_test"; - const input = argv[4] || "this is a test"; + const filename = argv[2] || 'wasm/hello.wasm'; + const funcname = argv[3] || 'run_test'; + const input = argv[4] || 'this is a test'; - const plugin = await createPlugin(filename, { - useWasi: true, - config: { "thing": "testing" }, - withAllowedHosts: ["*.typicode.com"] - }) + const plugin = await createPlugin(filename, { + useWasi: true, + config: { thing: 'testing' }, + withAllowedHosts: ['*.typicode.com'], + }); - const res = await plugin.call(funcname, new TextEncoder().encode(input)); - const s = new TextDecoder().decode(res.buffer); - console.log(s) + const res = await plugin.call(funcname, new TextEncoder().encode(input)); + const s = new TextDecoder().decode(res.buffer); + console.log(s); - await plugin.close() + await plugin.close(); } main(); diff --git a/justfile b/justfile index 75346f1..4161dd1 100644 --- a/justfile +++ b/justfile @@ -323,6 +323,18 @@ test-artifacts: lint *args: eslint src tests examples $args +format: + prettier --write src/*.ts src/**/*.ts examples/* + +docs: + typedoc src/mod.ts + +serve-docs: docs + python3 -m http.server 8000 -d docs/ + +watch-docs: prepare + watchexec -r -w src -w README.md just serve-docs + serve port='8124' logs='true': #!/usr/bin/env node diff --git a/src/background-plugin.ts b/src/background-plugin.ts index 6ac7fcb..b43ecfb 100644 --- a/src/background-plugin.ts +++ b/src/background-plugin.ts @@ -4,7 +4,7 @@ import { WORKER_URL } from 'js-sdk:worker-url'; import { Worker } from 'node:worker_threads'; import { FEATURES } from 'js-sdk:features'; -const MAX_WAIT = 5000 +const MAX_WAIT = 5000; enum SharedArrayBufferSection { End = 0, @@ -19,43 +19,40 @@ enum SharedArrayBufferSection { // // TODO: we should probably give _each_ background plugin its own waiter // script. -const AtomicsWaitAsync = Atomics.waitAsync || (() => { - const src = `onmessage = ev => { +const AtomicsWaitAsync = + Atomics.waitAsync || + (() => { + const src = `onmessage = ev => { const [b, i, v] = ev.data const f = new Int32Array(b) postMessage(Atomics.wait(f, i, v)); - }` - - const blob = new (Blob as any)([src], { type: 'text/javascript' }) - const url = URL.createObjectURL(blob) - const w = new Worker(url); - return (ia: any, index, value) => { - const promise = new Promise(resolve => { - w.once('message', data => { - resolve(data) - }) - }) - w.postMessage([ia.buffer, index, value]) - return promise - } -})() + }`; + + const blob = new (Blob as any)([src], { type: 'text/javascript' }); + const url = URL.createObjectURL(blob); + const w = new Worker(url); + return (ia: any, index, value) => { + const promise = new Promise((resolve) => { + w.once('message', (data) => { + resolve(data); + }); + }); + w.postMessage([ia.buffer, index, value]); + return promise; + }; + })(); class BackgroundPlugin { - worker: Worker - sharedData: SharedArrayBuffer - sharedDataView: DataView - hostFlag: Int32Array - opts: InternalConfig + worker: Worker; + sharedData: SharedArrayBuffer; + sharedDataView: DataView; + hostFlag: Int32Array; + opts: InternalConfig; #context: CallContext; #request: [(result: any[]) => void, (result: any[]) => void] | null = null; - constructor( - worker: Worker, - sharedData: SharedArrayBuffer, - opts: InternalConfig, - context: CallContext, - ) { + constructor(worker: Worker, sharedData: SharedArrayBuffer, opts: InternalConfig, context: CallContext) { this.worker = worker; this.sharedData = sharedData; this.sharedDataView = new DataView(sharedData); @@ -63,23 +60,26 @@ class BackgroundPlugin { this.opts = opts; this.#context = context; - this.worker.on('message', ev => this.#handleMessage(ev)) + this.worker.on('message', (ev) => this.#handleMessage(ev)); } async #handleMessage(ev: any) { switch (ev?.type) { - case 'invoke': return this.#handleInvoke(ev); - case 'return': return this.#handleReturn(ev); - case 'log': return this.#handleLog(ev); + case 'invoke': + return this.#handleInvoke(ev); + case 'return': + return this.#handleReturn(ev); + case 'log': + return this.#handleLog(ev); } } #handleLog(ev: any) { - const fn = (this.opts.logger as any)[ev.level as string] + const fn = (this.opts.logger as any)[ev.level as string]; if (typeof fn !== 'function') { - this.opts.logger?.error(`failed to find loglevel="${ev.level}" on logger: message=${ev.message}`) + this.opts.logger?.error(`failed to find loglevel="${ev.level}" on logger: message=${ev.message}`); } else { - fn.call(this.opts.logger, ev.message) + fn.call(this.opts.logger, ev.message); } } @@ -87,26 +87,26 @@ class BackgroundPlugin { const responder = this.#request || null; if (responder === null) { // This is fatal, we should probably panic - throw new Error(`received "return" call with no corresponding request`) + throw new Error(`received "return" call with no corresponding request`); } this.#request = null; - const [resolve, reject] = responder + const [resolve, reject] = responder; if (!Array.isArray(ev.results) || ev.results.length !== 2) { - return reject(new Error(`received malformed "return"`) as any) + return reject(new Error(`received malformed "return"`) as any); } - const [err, data] = ev.results + const [err, data] = ev.results; - err ? reject(err) : resolve(data) + err ? reject(err) : resolve(data); } // host -> guest() invoke async #invoke(handler: string, ...args: any[]): Promise { if (this.#request) { - throw new Error('plugin is not reentrant') + throw new Error('plugin is not reentrant'); } let resolve, reject; const promise = new Promise((res, rej) => { @@ -119,14 +119,14 @@ class BackgroundPlugin { this.worker.postMessage({ type: 'invoke', handler, - args - }) + args, + }); return promise; } async functionExists(funcName: string | [string, string]): Promise { - return await this.#invoke('functionExists', funcName) + return await this.#invoke('functionExists', funcName); } // host -> guest invoke() @@ -135,22 +135,22 @@ class BackgroundPlugin { const [errorIdx, outputIdx] = await this.callBlock(funcName, index); - const shouldThrow = errorIdx !== null - const idx = errorIdx ?? outputIdx + const shouldThrow = errorIdx !== null; + const idx = errorIdx ?? outputIdx; if (idx === null) { - return null + return null; } const block = this.#context[GET_BLOCK](idx); if (block === null) { - return null + return null; } let buf; if (FEATURES.allowSharedBufferCodec) { - buf = new Uint8Array(block.buffer) + buf = new Uint8Array(block.buffer); } else { // This platform doesn't support encoding/decoding SharedArrayBuffers. // Copy into a non-sharedarraybuffer so that the output works with TextDecoder. @@ -159,8 +159,8 @@ class BackgroundPlugin { } if (shouldThrow) { - const msg = new TextDecoder().decode(buf) - throw new Error(`Plugin-originated error: ${msg}`) + const msg = new TextDecoder().decode(buf); + throw new Error(`Plugin-originated error: ${msg}`); } return buf; @@ -171,12 +171,12 @@ class BackgroundPlugin { const { results, state } = await this.#invoke('call', funcName, input, exported); this.#context[IMPORT_STATE](state, true); - const [err, data] = results + const [err, data] = results; if (err) { - throw err + throw err; } - return data + return data; } // guest -> host invoke() @@ -188,81 +188,81 @@ class BackgroundPlugin { throw Error(`Plugin error: host function "${ev.namespace}" "${ev.func}" does not exist`); } - this.#context[IMPORT_STATE](ev.state, true) + this.#context[IMPORT_STATE](ev.state, true); - const data = await func(this.#context, ...ev.args) + const data = await func(this.#context, ...ev.args); - const { blocks } = this.#context[EXPORT_STATE]() + const { blocks } = this.#context[EXPORT_STATE](); // Writes to the ring buffer MAY return a promise if the write would wrap. // Writes that fit within the ring buffer return void. - const writer = new RingBufferWriter(this.sharedData) + const writer = new RingBufferWriter(this.sharedData); let promise: any; for (const [buffer, destination] of blocks) { - promise = writer.writeUint8(SharedArrayBufferSection.Block) + promise = writer.writeUint8(SharedArrayBufferSection.Block); if (promise) { - await promise + await promise; } - promise = writer.writeUint32(destination) + promise = writer.writeUint32(destination); if (promise) { - await promise + await promise; } - promise = writer.writeUint32(buffer?.byteLength || 0) + promise = writer.writeUint32(buffer?.byteLength || 0); if (promise) { - await promise + await promise; } if (buffer) { - promise = writer.write(new Uint8Array(buffer)) + promise = writer.write(new Uint8Array(buffer)); if (promise) { - await promise + await promise; } } } if (typeof data === 'bigint') { - promise = writer.writeUint8(SharedArrayBufferSection.RetI64) + promise = writer.writeUint8(SharedArrayBufferSection.RetI64); if (promise) { - await promise + await promise; } - promise = writer.writeUint64(data) + promise = writer.writeUint64(data); if (promise) { - await promise + await promise; } - } else if(typeof data === 'number') { - promise = writer.writeUint8(SharedArrayBufferSection.RetF64) + } else if (typeof data === 'number') { + promise = writer.writeUint8(SharedArrayBufferSection.RetF64); if (promise) { - await promise + await promise; } - promise = writer.writeFloat64(data) + promise = writer.writeFloat64(data); if (promise) { - await promise + await promise; } } else { - promise = writer.writeUint8(SharedArrayBufferSection.RetVoid) + promise = writer.writeUint8(SharedArrayBufferSection.RetVoid); if (promise) { - await promise + await promise; } } - await writer.writeUint8(SharedArrayBufferSection.End) as any || writer.flush() + ((await writer.writeUint8(SharedArrayBufferSection.End)) as any) || writer.flush(); } catch (err) { this.close(); - const [, reject] = this.#request as any[] - this.#request = null - return reject(err) + const [, reject] = this.#request as any[]; + this.#request = null; + return reject(err); } } async getExports(name?: string): Promise { - return await this.#invoke('getExports', name ?? '0') + return await this.#invoke('getExports', name ?? '0'); } async getImports(name?: string): Promise { - return await this.#invoke('getImports', name ?? '0') + return await this.#invoke('getImports', name ?? '0'); } async getInstance(): Promise { @@ -288,12 +288,12 @@ export async function createBackgroundPlugin( await new Promise((resolve, reject) => { worker.on('message', function handler(ev) { if (ev?.type !== 'initialized') { - reject(new Error(`received unexpected message (type=${ev?.type})`)) + reject(new Error(`received unexpected message (type=${ev?.type})`)); } worker.removeListener('message', handler); resolve(null); - }) + }); }); // NB(chrisdickinson): We *have* to create the SharedArrayBuffer in @@ -306,9 +306,7 @@ export async function createBackgroundPlugin( const message = { ...rest, type: 'init', - functions: Object.fromEntries( - Object.entries(opts.functions || {}).map(([k, v]) => [k, Object.keys(v)]) - ), + functions: Object.fromEntries(Object.entries(opts.functions || {}).map(([k, v]) => [k, Object.keys(v)])), names, modules, sharedData, @@ -317,103 +315,98 @@ export async function createBackgroundPlugin( const onready = new Promise((resolve, reject) => { worker.on('message', function handler(ev) { if (ev?.type !== 'ready') { - reject(new Error(`received unexpected message (type=${ev?.type})`)) + reject(new Error(`received unexpected message (type=${ev?.type})`)); } worker.removeListener('message', handler); resolve(null); - }) + }); }); worker.postMessage(message, modules); - await onready + await onready; - return new BackgroundPlugin( - worker, - sharedData, - opts, - context - ); + return new BackgroundPlugin(worker, sharedData, opts, context); } class RingBufferWriter { - output: SharedArrayBuffer - scratch: ArrayBuffer - scratchView: DataView - outputOffset: number + output: SharedArrayBuffer; + scratch: ArrayBuffer; + scratchView: DataView; + outputOffset: number; constructor(output: SharedArrayBuffer) { - this.scratch = new ArrayBuffer(8) - this.scratchView = new DataView(this.scratch) + this.scratch = new ArrayBuffer(8); + this.scratchView = new DataView(this.scratch); this.output = output; this.outputOffset = 4; } async spanningWrite(input: Uint8Array) { - let outputOffset = this.outputOffset - let inputOffset = 0 - let toWrite = this.output.byteLength - this.outputOffset - let flushedWriteCount = 1 + Math.floor((input.byteLength - toWrite) / this.output.byteLength) + let outputOffset = this.outputOffset; + let inputOffset = 0; + let toWrite = this.output.byteLength - this.outputOffset; + let flushedWriteCount = 1 + Math.floor((input.byteLength - toWrite) / this.output.byteLength); const finalWrite = (input.byteLength - toWrite) % this.output.byteLength; do { - new Uint8Array(this.output).set(input.subarray(inputOffset, toWrite), outputOffset) - await this.flush() - inputOffset += toWrite - outputOffset = 4 - toWrite = this.output.byteLength - 4 - --flushedWriteCount - } while(flushedWriteCount != 0) + new Uint8Array(this.output).set(input.subarray(inputOffset, toWrite), outputOffset); + await this.flush(); + inputOffset += toWrite; + outputOffset = 4; + toWrite = this.output.byteLength - 4; + --flushedWriteCount; + } while (flushedWriteCount != 0); if (finalWrite) { - new Uint8Array(this.output).set(input.subarray(inputOffset, finalWrite), outputOffset) - outputOffset += finalWrite + new Uint8Array(this.output).set(input.subarray(inputOffset, finalWrite), outputOffset); + outputOffset += finalWrite; } - this.outputOffset = outputOffset + this.outputOffset = outputOffset; } write(bytes: ArrayBufferLike): void | Promise { if (bytes.byteLength + this.outputOffset < this.output.byteLength) { - new Uint8Array(this.output).set(new Uint8Array(bytes), this.outputOffset) - this.outputOffset += bytes.byteLength - return + new Uint8Array(this.output).set(new Uint8Array(bytes), this.outputOffset); + this.outputOffset += bytes.byteLength; + return; } - return this.spanningWrite(new Uint8Array(bytes)) + return this.spanningWrite(new Uint8Array(bytes)); } writeUint8(value: number): void | Promise { - this.scratchView.setUint8(0, value) - return this.write(this.scratch.slice(0, 1)) + this.scratchView.setUint8(0, value); + return this.write(this.scratch.slice(0, 1)); } writeUint32(value: number): void | Promise { - this.scratchView.setUint32(0, value, true) - return this.write(this.scratch.slice(0, 4)) + this.scratchView.setUint32(0, value, true); + return this.write(this.scratch.slice(0, 4)); } writeUint64(value: bigint): void | Promise { - this.scratchView.setBigUint64(0, value, true) - return this.write(this.scratch.slice(0, 8)) + this.scratchView.setBigUint64(0, value, true); + return this.write(this.scratch.slice(0, 8)); } writeFloat64(value: number): void | Promise { - this.scratchView.setFloat64(0, value, true) - return this.write(this.scratch.slice(0, 8)) + this.scratchView.setFloat64(0, value, true); + return this.write(this.scratch.slice(0, 8)); } async flush() { - const flag = new Int32Array(this.output) + const flag = new Int32Array(this.output); - Atomics.store(flag, 0, this.outputOffset) - Atomics.notify(flag, 0) - const result = AtomicsWaitAsync(flag, 0, this.outputOffset, MAX_WAIT) + Atomics.store(flag, 0, this.outputOffset); + Atomics.notify(flag, 0); + const result = AtomicsWaitAsync(flag, 0, this.outputOffset, MAX_WAIT); if (result.async) { - result.value = await result.value as any + result.value = (await result.value) as any; } if (result.value === 'timed-out') { - throw new Error('encountered timeout while flushing host function to worker memory') + throw new Error('encountered timeout while flushing host function to worker memory'); } } } diff --git a/src/call-context.ts b/src/call-context.ts index 150b51f..3ca6427 100644 --- a/src/call-context.ts +++ b/src/call-context.ts @@ -1,27 +1,27 @@ import { type PluginConfig } from './mod.ts'; import { FEATURES } from 'js-sdk:features'; -export const BEGIN = Symbol('begin') -export const END = Symbol('end') -export const ENV = Symbol('env') -export const GET_BLOCK = Symbol('get-block') -export const IMPORT_STATE = Symbol('import-state') -export const EXPORT_STATE = Symbol('export-state') -export const STORE = Symbol('store-value') +export const BEGIN = Symbol('begin'); +export const END = Symbol('end'); +export const ENV = Symbol('env'); +export const GET_BLOCK = Symbol('get-block'); +export const IMPORT_STATE = Symbol('import-state'); +export const EXPORT_STATE = Symbol('export-state'); +export const STORE = Symbol('store-value'); export class Block { - buffer: ArrayBufferLike - view: DataView - local: boolean + buffer: ArrayBufferLike; + view: DataView; + local: boolean; get byteLength() { - return this.buffer.byteLength + return this.buffer.byteLength; } constructor(arrayBuffer: ArrayBufferLike, local: boolean) { - this.buffer = arrayBuffer - this.view = new DataView(this.buffer) - this.local = local + this.buffer = arrayBuffer; + this.view = new DataView(this.buffer); + this.local = local; } static indexToAddress(idx: bigint | number): bigint { @@ -37,29 +37,31 @@ export class Block { } } -export type CallState = { blocks: [ArrayBufferLike | null, number][], stack: [number | null, number | null, number | null][] }; +export type CallState = { + blocks: [ArrayBufferLike | null, number][]; + stack: [number | null, number | null, number | null][]; +}; export class CallContext { #stack: [number | null, number | null, number | null][]; #blocks: (Block | null)[] = []; - #logger: Console + #logger: Console; #decoder: TextDecoder; #encoder: TextEncoder; - #arrayBufferType: { new(size: number): ArrayBufferLike ;} + #arrayBufferType: { new (size: number): ArrayBufferLike }; #config: PluginConfig; readString(addr: bigint | number): string | null { const blockIdx = Block.addressToIndex(addr); const block = this.#blocks[blockIdx]; if (!block) { - return null + return null; } - const buffer = ( - (!(block.buffer instanceof ArrayBuffer) && !FEATURES.allowSharedBufferCodec) - ? new Uint8Array(block.buffer).slice().buffer - : block.buffer - ); + const buffer = + !(block.buffer instanceof ArrayBuffer) && !FEATURES.allowSharedBufferCodec + ? new Uint8Array(block.buffer).slice().buffer + : block.buffer; return this.#decoder.decode(buffer); } @@ -67,15 +69,15 @@ export class CallContext { store(input: string | Uint8Array | number): bigint { const idx = this[STORE](input); if (!idx) { - throw new Error('failed to store output') + throw new Error('failed to store output'); } - return Block.indexToAddress(idx) + return Block.indexToAddress(idx); } [ENV] = { extism_alloc: (n: bigint): bigint => { - const block = this.alloc(n) - const addr = Block.indexToAddress(block) + const block = this.alloc(n); + const addr = Block.indexToAddress(block); return addr; }, @@ -127,13 +129,13 @@ export class CallContext { extism_output_set: (addr: bigint, length: bigint) => { const blockIdx = Block.addressToIndex(addr); - const block = this.#blocks[blockIdx] + const block = this.#blocks[blockIdx]; if (!block) { - throw new Error('cannot assign to this block') + throw new Error('cannot assign to this block'); } if (length > block.buffer.byteLength) { - throw new Error('length longer than target block') + throw new Error('length longer than target block'); } this.#stack[this.#stack.length - 1][1] = blockIdx; @@ -141,9 +143,9 @@ export class CallContext { extism_error_set: (addr: bigint) => { const blockIdx = Block.addressToIndex(addr); - const block = this.#blocks[blockIdx] + const block = this.#blocks[blockIdx]; if (!block) { - throw new Error('cannot assign to this block') + throw new Error('cannot assign to this block'); } this.#stack[this.#stack.length - 1][2] = blockIdx; @@ -153,29 +155,28 @@ export class CallContext { const key = this.readString(addr); if (key === null) { - return 0n + return 0n; } if (key in this.#config) { - return this.store(this.#config[key]) + return this.store(this.#config[key]); } - return 0n + return 0n; }, extism_var_get(_i: bigint): bigint { - return 0n + return 0n; }, - extism_var_set(_n: bigint, _i: bigint) { - }, + extism_var_set(_n: bigint, _i: bigint) {}, extism_http_request(_requestOffset: bigint, _bodyOffset: bigint): bigint { - return 0n + return 0n; }, extism_http_status_code(): number { - return 0 + return 0; }, extism_length: (addr: bigint): bigint => { @@ -184,14 +185,16 @@ export class CallContext { if (!block) { return 0n; } - return BigInt(block.buffer.byteLength) + return BigInt(block.buffer.byteLength); }, extism_log_warn: (addr: bigint) => { const blockIdx = Block.addressToIndex(addr); const block = this.#blocks[blockIdx]; if (!block) { - return this.#logger.error(`failed to log(warn): bad block reference in addr 0x${addr.toString(16).padStart(64, '0')}`) + return this.#logger.error( + `failed to log(warn): bad block reference in addr 0x${addr.toString(16).padStart(64, '0')}`, + ); } const text = this.#decoder.decode(block.buffer); this.#logger.warn(text); @@ -201,7 +204,9 @@ export class CallContext { const blockIdx = Block.addressToIndex(addr); const block = this.#blocks[blockIdx]; if (!block) { - return this.#logger.error(`failed to log(info): bad block reference in addr 0x${addr.toString(16).padStart(64, '0')}`) + return this.#logger.error( + `failed to log(info): bad block reference in addr 0x${addr.toString(16).padStart(64, '0')}`, + ); } const text = this.#decoder.decode(block.buffer); this.#logger.info(text); @@ -211,7 +216,9 @@ export class CallContext { const blockIdx = Block.addressToIndex(addr); const block = this.#blocks[blockIdx]; if (!block) { - return this.#logger.error(`failed to log(debug): bad block reference in addr 0x${addr.toString(16).padStart(64, '0')}`) + return this.#logger.error( + `failed to log(debug): bad block reference in addr 0x${addr.toString(16).padStart(64, '0')}`, + ); } const text = this.#decoder.decode(block.buffer); this.#logger.debug(text); @@ -221,36 +228,38 @@ export class CallContext { const blockIdx = Block.addressToIndex(addr); const block = this.#blocks[blockIdx]; if (!block) { - return this.#logger.error(`failed to log(error): bad block reference in addr 0x${addr.toString(16).padStart(64, '0')}`) + return this.#logger.error( + `failed to log(error): bad block reference in addr 0x${addr.toString(16).padStart(64, '0')}`, + ); } const text = this.#decoder.decode(block.buffer); this.#logger.error(text); }, - } + }; get #input(): Block | null { - const idx = this.#stack[this.#stack.length - 1][0] + const idx = this.#stack[this.#stack.length - 1][0]; if (idx === null) { - return null + return null; } - return this.#blocks[idx] + return this.#blocks[idx]; } - constructor(type: { new(size: number): ArrayBufferLike ;}, logger: Console, config: PluginConfig) { - this.#arrayBufferType = type - this.#logger = logger - this.#decoder = new TextDecoder() - this.#encoder = new TextEncoder() - this.#stack = [] + constructor(type: { new (size: number): ArrayBufferLike }, logger: Console, config: PluginConfig) { + this.#arrayBufferType = type; + this.#logger = logger; + this.#decoder = new TextDecoder(); + this.#encoder = new TextEncoder(); + this.#stack = []; // reserve the null page. - this.alloc(1) + this.alloc(1); - this.#config = config + this.#config = config; } alloc(size: bigint | number): number { - const block = new Block(new (this.#arrayBufferType)(Number(size)), true); + const block = new Block(new this.#arrayBufferType(Number(size)), true); const index = this.#blocks.length; this.#blocks.push(block); return index; @@ -259,45 +268,47 @@ export class CallContext { [GET_BLOCK](index: number): Block { const block = this.#blocks[index]; if (!block) { - throw new Error(`invalid block index: ${index}`) + throw new Error(`invalid block index: ${index}`); } - return block + return block; } [IMPORT_STATE](state: CallState, copy: boolean = false) { // eslint-disable-next-line prefer-const for (let [buf, idx] of state.blocks) { if (buf && copy) { - const dst = new Uint8Array(new (this.#arrayBufferType)(Number(buf.byteLength))); - dst.set(new Uint8Array(buf)) - buf = dst.buffer + const dst = new Uint8Array(new this.#arrayBufferType(Number(buf.byteLength))); + dst.set(new Uint8Array(buf)); + buf = dst.buffer; } - this.#blocks[idx] = buf ? new Block(buf, false) : null + this.#blocks[idx] = buf ? new Block(buf, false) : null; } - this.#stack = state.stack + this.#stack = state.stack; } [EXPORT_STATE](): CallState { return { stack: this.#stack.slice(), - blocks: this.#blocks.map((block, idx) => { - if (!block) { - return [null, idx] - } - - if (block.local) { - block.local = false - return [block.buffer, idx] - } - return null - }).filter(Boolean) as [ArrayBufferLike, number][] - } + blocks: this.#blocks + .map((block, idx) => { + if (!block) { + return [null, idx]; + } + + if (block.local) { + block.local = false; + return [block.buffer, idx]; + } + return null; + }) + .filter(Boolean) as [ArrayBufferLike, number][], + }; } [STORE](input?: string | Uint8Array | number) { if (!input) { - return null - } + return null; + } if (typeof input === 'string') { input = this.#encoder.encode(input); @@ -306,43 +317,43 @@ export class CallContext { if (input instanceof Uint8Array) { if (input.buffer.constructor === this.#arrayBufferType) { // no action necessary, wrap it up in a block - const idx = this.#blocks.length - this.#blocks.push(new Block(input.buffer, true)) - return idx + const idx = this.#blocks.length; + this.#blocks.push(new Block(input.buffer, true)); + return idx; } const idx = this.alloc(input.length); const block = this.#blocks[idx] as Block; const buf = new Uint8Array(block.buffer); buf.set(input, 0); - return idx + return idx; } - return input + return input; } [BEGIN](input: number | null) { - this.#stack.push([input, null, null]) + this.#stack.push([input, null, null]); } [END](): [number | null, number | null] { const [, outputIdx, errorIdx] = this.#stack.pop() as (number | null)[]; - const outputPosition = errorIdx === null ? 1 : 0 - const idx = errorIdx ?? outputIdx - const result: [number | null, number | null] = [null, null] + const outputPosition = errorIdx === null ? 1 : 0; + const idx = errorIdx ?? outputIdx; + const result: [number | null, number | null] = [null, null]; if (idx === null) { - return result + return result; } - const block = this.#blocks[idx] + const block = this.#blocks[idx]; if (block === null) { // TODO: this might be an error? we got an output idx but it referred to a freed (or non-existant) block - return result + return result; } - result[outputPosition] = idx + result[outputPosition] = idx; - return result + return result; } } diff --git a/src/foreground-plugin.ts b/src/foreground-plugin.ts index cebffa2..beea647 100644 --- a/src/foreground-plugin.ts +++ b/src/foreground-plugin.ts @@ -5,11 +5,15 @@ import { loadWasi } from 'js-sdk:wasi'; const DYLIBSO_ENV = 'env'; class ForegroundPlugin { - #context: CallContext - #modules: { guestType: string, module: WebAssembly.WebAssemblyInstantiatedSource}[] - #names: string[] - - constructor(context: CallContext, names: string[], modules: { guestType: string, module: WebAssembly.WebAssemblyInstantiatedSource}[]) { + #context: CallContext; + #modules: { guestType: string; module: WebAssembly.WebAssemblyInstantiatedSource }[]; + #names: string[]; + + constructor( + context: CallContext, + names: string[], + modules: { guestType: string; module: WebAssembly.WebAssemblyInstantiatedSource }[], + ) { this.#context = context; this.#names = names; this.#modules = modules; @@ -18,46 +22,50 @@ class ForegroundPlugin { async functionExists(funcName: string | [string, string]): Promise { try { const search: string[] = [].concat(funcName); - const [target, name] = ( - search.length === 2 ? - [this.lookupTarget(search[0]), search[1]] : - [this.#modules.find(guest => { - const exports = WebAssembly.Module.exports(guest.module.module) - return exports.find(item => { - return item.name === search[0] && item.kind === 'function' - }) - }), search[0]] - ) + const [target, name] = + search.length === 2 + ? [this.lookupTarget(search[0]), search[1]] + : [ + this.#modules.find((guest) => { + const exports = WebAssembly.Module.exports(guest.module.module); + return exports.find((item) => { + return item.name === search[0] && item.kind === 'function'; + }); + }), + search[0], + ]; if (!target) { - return false + return false; } const func = target.module.instance.exports[name] as any; if (!func) { - return false + return false; } - return true + return true; } catch { // lookupTarget will throw if it cannot find the specified target; cast it into a boolean - return false + return false; } } async callBlock(funcName: string | [string, string], input: number | null): Promise<[number | null, number | null]> { const search: string[] = [].concat(funcName); - const [target, name] = ( - search.length === 2 ? - [this.lookupTarget(search[0]), search[1]] : - [this.#modules.find(guest => { - const exports = WebAssembly.Module.exports(guest.module.module) - return exports.find(item => { - return item.name === search[0] && item.kind === 'function' - }) - }), search[0]] - ) + const [target, name] = + search.length === 2 + ? [this.lookupTarget(search[0]), search[1]] + : [ + this.#modules.find((guest) => { + const exports = WebAssembly.Module.exports(guest.module.module); + return exports.find((item) => { + return item.name === search[0] && item.kind === 'function'; + }); + }), + search[0], + ]; if (!target) { throw Error(`Plugin error: target "${search.join('" "')}" does not exist`); @@ -67,58 +75,58 @@ class ForegroundPlugin { throw Error(`Plugin error: function "${search.join('" "')}" does not exist`); } - this.#context[BEGIN](input ?? null) + this.#context[BEGIN](input ?? null); try { func(); - return this.#context[END]() - } catch(err) { + return this.#context[END](); + } catch (err) { this.#context[END](); - throw err + throw err; } } async call(funcName: string | [string, string], input?: string | Uint8Array): Promise { const inputIdx = this.#context[STORE](input); const [errorIdx, outputIdx] = await this.callBlock(funcName, inputIdx); - const shouldThrow = errorIdx !== null - const idx = errorIdx ?? outputIdx + const shouldThrow = errorIdx !== null; + const idx = errorIdx ?? outputIdx; if (idx === null) { - return null + return null; } const block = this.#context[GET_BLOCK](idx); if (!block) { - return null + return null; } - const buf = new Uint8Array(block.buffer) + const buf = new Uint8Array(block.buffer); if (shouldThrow) { - const msg = new TextDecoder().decode(buf) - throw new Error(`Plugin-originated error: ${msg}`) + const msg = new TextDecoder().decode(buf); + throw new Error(`Plugin-originated error: ${msg}`); } - return buf + return buf; } - private lookupTarget(name: any): {guestType: string, module: WebAssembly.WebAssemblyInstantiatedSource} { - const target = String(name ?? '0') - const idx = this.#names.findIndex(xs => xs === target); + private lookupTarget(name: any): { guestType: string; module: WebAssembly.WebAssemblyInstantiatedSource } { + const target = String(name ?? '0'); + const idx = this.#names.findIndex((xs) => xs === target); if (idx === -1) { throw new Error(`no module named "${name}"`); } - return this.#modules[idx] + return this.#modules[idx]; } async getExports(name?: string): Promise { - return WebAssembly.Module.exports(this.lookupTarget(name).module.module) || [] + return WebAssembly.Module.exports(this.lookupTarget(name).module.module) || []; } async getImports(name?: string): Promise { - return WebAssembly.Module.imports(this.lookupTarget(name).module.module) || [] + return WebAssembly.Module.imports(this.lookupTarget(name).module.module) || []; } async getInstance(name?: string): Promise { - return this.lookupTarget(name).module.instance + return this.lookupTarget(name).module.instance; } async close(): Promise { @@ -130,14 +138,14 @@ export async function createForegroundPlugin( opts: InternalConfig, names: string[], sources: ArrayBuffer[], - context: CallContext = new CallContext(ArrayBuffer, opts.logger, opts.config) + context: CallContext = new CallContext(ArrayBuffer, opts.logger, opts.config), ): Promise { const wasi = opts.wasiEnabled ? await loadWasi(opts.allowedPaths) : null; const imports: Record> = { - ...(wasi ? {wasi_snapshot_preview1: await wasi.importObject()} : {}), + ...(wasi ? { wasi_snapshot_preview1: await wasi.importObject() } : {}), [DYLIBSO_ENV]: context[ENV], - } + }; for (const namespace in opts.functions) { imports[namespace] = imports[namespace] || {}; @@ -146,31 +154,35 @@ export async function createForegroundPlugin( } } - const modules = await Promise.all(sources.map(async (source, idx, all) => { - const isRuntime = idx === all.length; - const module = await WebAssembly.instantiate(source, (isRuntime ? {} : imports) as WebAssembly.Imports); - if (!isRuntime && module.instance.exports._start) { - await wasi?.initialize(module.instance); - } - - const guestType = ( - module.instance.exports._initialize ? 'reactor' : - module.instance.exports.hs_init ? 'haskell' : - module.instance.exports.__wasm_call_ctors ? 'command' : - 'none' - ); - - const init: any = ( - module.instance.exports._initialize ? module.instance.exports._initialize : - module.instance.exports.hs_init ? module.instance.exports.hs_init : - module.instance.exports.__wasm_call_ctors ? module.instance.exports.__wasm_call_ctors : - () => {} - ); - - init() + const modules = await Promise.all( + sources.map(async (source, idx, all) => { + const isRuntime = idx === all.length; + const module = await WebAssembly.instantiate(source, (isRuntime ? {} : imports) as WebAssembly.Imports); + if (!isRuntime && module.instance.exports._start) { + await wasi?.initialize(module.instance); + } - return { module, guestType } - })); + const guestType = module.instance.exports._initialize + ? 'reactor' + : module.instance.exports.hs_init + ? 'haskell' + : module.instance.exports.__wasm_call_ctors + ? 'command' + : 'none'; + + const init: any = module.instance.exports._initialize + ? module.instance.exports._initialize + : module.instance.exports.hs_init + ? module.instance.exports.hs_init + : module.instance.exports.__wasm_call_ctors + ? module.instance.exports.__wasm_call_ctors + : () => {}; + + init(); + + return { module, guestType }; + }), + ); return new ForegroundPlugin(context, names, modules); } diff --git a/src/manifest.ts b/src/manifest.ts index 90e3cae..e880905 100644 --- a/src/manifest.ts +++ b/src/manifest.ts @@ -25,11 +25,7 @@ export interface ManifestWasmPath { /** * The WASM to load as bytes, a path, or a url */ -export type ManifestWasm = ( - ManifestWasmUrl | - ManifestWasmData | - ManifestWasmPath -) & { +export type ManifestWasm = (ManifestWasmUrl | ManifestWasmData | ManifestWasmPath) & { name?: string | undefined; hash?: string | undefined; }; @@ -71,7 +67,7 @@ async function _populateWasmField(candidate: ManifestLike, _fetch: typeof fetch) } if (candidate.search(/^(https?|file):\/\//) !== 0) { - return { wasm: [{ path: candidate }] } + return { wasm: [{ path: candidate }] }; } candidate = new URL(candidate); @@ -90,85 +86,87 @@ async function _populateWasmField(candidate: ManifestLike, _fetch: typeof fetch) return _populateWasmField(JSON.parse(await response.text()), _fetch); default: throw new TypeError( - `While processing manifest URL "${candidate}"; expected content-type of "text/json", "application/json", "application/octet-stream", or "application/wasm"; got "${contentType}" after stripping off charset.` - ) + `While processing manifest URL "${candidate}"; expected content-type of "text/json", "application/json", "application/octet-stream", or "application/wasm"; got "${contentType}" after stripping off charset.`, + ); } } if (!('wasm' in candidate)) { - throw new TypeError('Expected "wasm" key in manifest') + throw new TypeError('Expected "wasm" key in manifest'); } if (!Array.isArray(candidate.wasm)) { - throw new TypeError('Expected "manifest.wasm" to be array') + throw new TypeError('Expected "manifest.wasm" to be array'); } - const badItemIdx = candidate.wasm.findIndex( - item => !('data' in item) && !('url' in item) && !('path' in item) - ); + const badItemIdx = candidate.wasm.findIndex((item) => !('data' in item) && !('url' in item) && !('path' in item)); if (badItemIdx > -1) { - throw new TypeError(`Expected every item in "manifest.wasm" to include either a "data", "url", or "path" key; got bad item at index ${badItemIdx}`) + throw new TypeError( + `Expected every item in "manifest.wasm" to include either a "data", "url", or "path" key; got bad item at index ${badItemIdx}`, + ); } - return {...candidate as Manifest}; + return { ...(candidate as Manifest) }; } export async function intoManifest(candidate: ManifestLike, _fetch: typeof fetch = fetch): Promise { - const manifest = await _populateWasmField(candidate, _fetch) as Manifest; + const manifest = (await _populateWasmField(candidate, _fetch)) as Manifest; manifest.config ??= {}; - manifest.allowed_hosts = [].concat(manifest.allowed_hosts || [] as any); + manifest.allowed_hosts = [].concat(manifest.allowed_hosts || ([] as any)); return manifest; } export async function toWasmModuleData(manifest: Manifest, _fetch: typeof fetch): Promise<[string[], ArrayBuffer[]]> { const names: string[] = []; - const manifestsWasm = await Promise.all(manifest.wasm.map(async (item, idx) => { - let buffer: ArrayBuffer; - if ((item as ManifestWasmData).data) { - const data = (item as ManifestWasmData).data; + const manifestsWasm = await Promise.all( + manifest.wasm.map(async (item, idx) => { + let buffer: ArrayBuffer; + if ((item as ManifestWasmData).data) { + const data = (item as ManifestWasmData).data; - if ((data as Uint8Array).buffer) { - buffer = data.buffer; + if ((data as Uint8Array).buffer) { + buffer = data.buffer; + } else { + buffer = data as ArrayBuffer; + } + } else if ((item as ManifestWasmPath).path) { + const path = (item as ManifestWasmPath).path; + const data = await readFile(path); + buffer = data.buffer as ArrayBuffer; } else { - buffer = data as ArrayBuffer; + const response = await _fetch((item as ManifestWasmUrl).url, { + headers: { + accept: 'application/wasm;q=0.9,application/octet-stream;q=0.8', + 'user-agent': 'extism', + }, + }); + + buffer = await response.arrayBuffer(); } - } else if ((item as ManifestWasmPath).path) { - const path = (item as ManifestWasmPath).path; - const data = await readFile(path); - buffer = data.buffer as ArrayBuffer; - } else { - const response = await _fetch((item as ManifestWasmUrl).url, { - headers: { - accept: 'application/wasm;q=0.9,application/octet-stream;q=0.8', - 'user-agent': 'extism' - } - }) - buffer = await response.arrayBuffer(); - } + if (item.hash) { + const hashBuffer = new Uint8Array(await crypto.subtle.digest('SHA-256', buffer)); + const checkBuffer = new Uint8Array(32); + let eq = true; + for (let i = 0; i < 32; ++i) { + checkBuffer[i] = parseInt(item.hash.slice(i << 1, (i << 1) + 2), 16); + // do not exit early: we want to do a constant time comparison + eq = eq && checkBuffer[i] === hashBuffer[i]; + } + const hashAsString = () => [...hashBuffer].map((xs) => xs.toString(16).padStart(2, '0')).join(''); - if (item.hash) { - const hashBuffer = new Uint8Array(await crypto.subtle.digest('SHA-256', buffer)); - const checkBuffer = new Uint8Array(32); - let eq = true; - for (let i = 0; i < 32; ++i) { - checkBuffer[i] = parseInt(item.hash.slice(i<<1, (i<<1) + 2), 16); - // do not exit early: we want to do a constant time comparison - eq = eq && (checkBuffer[i] === hashBuffer[i]); - } - const hashAsString = () => [...hashBuffer].map(xs => xs.toString(16).padStart(2, '0')).join(''); + if (!eq) { + throw new Error(`Plugin error: hash mismatch. Expected: ${item.hash}. Actual: ${hashAsString()}`); + } - if (!eq) { - throw new Error(`Plugin error: hash mismatch. Expected: ${item.hash}. Actual: ${hashAsString()}`); + item.name ??= hashAsString(); } - item.name ??= hashAsString(); - } - - (names[idx]) = item.name ?? String(idx); - return buffer - })); + (names[idx]) = item.name ?? String(idx); + return buffer; + }), + ); - return [names, manifestsWasm] + return [names, manifestsWasm]; } diff --git a/src/mod.test.ts b/src/mod.test.ts index 6feafd0..a6ff12a 100644 --- a/src/mod.test.ts +++ b/src/mod.test.ts @@ -2,317 +2,344 @@ import { test } from 'node:test'; import assert from 'node:assert'; import createPlugin, { CallContext, FEATURES } from './mod.ts'; -// The presence of `*.test.ts` files adjacent to module files is no mistake, sadly: -// we have to be in the same directory in order to preserve the `__dirname` / `import.meta.url` value -// between `mod.ts` and the tests in the build output. -test('createPlugin loads a module and provides lookups', async () => { - const plugin = await createPlugin('http://localhost:8124/wasm/code.wasm', { useWasi: true }); - - try { - assert(await plugin.functionExists('count_vowels'), 'count_vowels should exist'); - assert(await plugin.functionExists(['0', 'count_vowels']), '0:count_vowels should exist'); - assert(!await plugin.functionExists(['dne', 'count_vowels']), 'dne:count_vowels should not exist'); - assert(!await plugin.functionExists('count_sheep'), 'count_sheep should not exist'); - } finally { - await plugin.close(); - } -}); - -if (!FEATURES.crossOriginChecksEnforced) { - test('can create plugin from url with hash check', async () => { - const plugin = await createPlugin({wasm: [{ - url: "https://raw.githubusercontent.com/extism/extism/main/wasm/code.wasm", - hash: "7def5bb4aa3843a5daf5d6078f1e8540e5ef10b035a9d9387e9bd5156d2b2565" - }]}); +if (typeof WebAssembly === 'undefined') { + test('this platform lacks WebAssembly support', async () => { + // at the time of writing (2023 Oct 27), playwright webkit builds for windows + // do not support webassembly. there's an open PR (https://github.com/WebKit/WebKit/pull/18184) + // to fix this though. + }); +} else { + // The presence of `*.test.ts` files adjacent to module files is no mistake, sadly: + // we have to be in the same directory in order to preserve the `__dirname` / `import.meta.url` value + // between `mod.ts` and the tests in the build output. + test('createPlugin loads a module and provides lookups', async () => { + const plugin = await createPlugin('http://localhost:8124/wasm/code.wasm', { useWasi: true }); try { - assert.equal(await plugin.functionExists('count_vowels'), true); + assert(await plugin.functionExists('count_vowels'), 'count_vowels should exist'); + assert(await plugin.functionExists(['0', 'count_vowels']), '0:count_vowels should exist'); + assert(!(await plugin.functionExists(['dne', 'count_vowels'])), 'dne:count_vowels should not exist'); + assert(!(await plugin.functionExists('count_sheep')), 'count_sheep should not exist'); } finally { - await plugin.close() + await plugin.close(); } }); -} -test('createPlugin fails on hash mismatch (bad hash)', async () => { - const [err, plugin] = await createPlugin({ - wasm: [{ - url: 'http://localhost:8124/wasm/code.wasm', - hash: 'not a good hash' - }] - }, { useWasi: true }).then( - data => [null, data], - err => [err, null] - ); - - assert(plugin === null); - assert(/hash mismatch/.test(err.message)) - if (plugin) { - await plugin.close() - } -}) - -test('createPlugin fails on hash mismatch (hash mismatch)', async () => { - const [err, plugin] = await createPlugin({ - wasm: [{ - url: 'http://localhost:8124/wasm/code.wasm', - hash: '7def5bb4aa3843a5daf5d6078f1e8540e5ef10b035a9d9387e9bd5156d2b256a' - }] - }, { useWasi: true }).then( - data => [null, data], - err => [err, null] - ); - - assert(plugin === null); - assert(/hash mismatch/.test(err.message)) - if (plugin) { - await plugin.close() - } -}); - -test('createPlugin loads a module and provides access to exports/imports', async () => { - const plugin = await createPlugin({wasm: [ - { url: 'http://localhost:8124/wasm/code.wasm' } - ]}, { useWasi: true }); - - try { - const exports = await plugin.getExports(); - assert.deepEqual(exports.map(xs => xs.name).sort(), ['memory', 'count_vowels', '__data_end', '__heap_base'].sort()); - - const imports = await plugin.getImports(); - assert.deepEqual(imports.map(xs => xs.name).sort(), ['extism_alloc', - 'extism_output_set', - 'extism_input_length', - 'extism_input_load_u64', - 'extism_input_load_u8', - 'extism_store_u64', - 'extism_store_u8'].sort()); - } finally { - await plugin.close(); + if (!FEATURES.crossOriginChecksEnforced) { + test('can create plugin from url with hash check', async () => { + const plugin = await createPlugin({ + wasm: [ + { + url: 'https://raw.githubusercontent.com/extism/extism/main/wasm/code.wasm', + hash: '7def5bb4aa3843a5daf5d6078f1e8540e5ef10b035a9d9387e9bd5156d2b2565', + }, + ], + }); + + try { + assert.equal(await plugin.functionExists('count_vowels'), true); + } finally { + await plugin.close(); + } + }); } -}); -test('createPlugin returns an interface that can call wasm functions', async () => { - const plugin = await createPlugin({wasm: [ - { url: 'http://localhost:8124/wasm/code.wasm' } - ]}, { useWasi: true }); + test('createPlugin fails on hash mismatch (bad hash)', async () => { + const [err, plugin] = await createPlugin( + { + wasm: [ + { + url: 'http://localhost:8124/wasm/code.wasm', + hash: 'not a good hash', + }, + ], + }, + { useWasi: true }, + ).then( + (data) => [null, data], + (err) => [err, null], + ); - try { - const result = await plugin.call('count_vowels', 'hello world'); - assert(result, 'result is not null'); + assert(plugin === null); + assert(/hash mismatch/.test(err.message)); + if (plugin) { + await plugin.close(); + } + }); - assert.deepEqual(JSON.parse(new TextDecoder().decode(result.buffer)), { count: 3 }) - } finally { - await plugin.close(); - } -}); - -test('logging works as expected', async () => { - const intercept: Record = {}; - const logLevel = (level: string) => (message: string) => intercept[level] = message - - // FIXME: we're using non-blocking log functions here; to properly preserve behavior we - // should invoke these and wait on the host to return. - const logger = Object.fromEntries([ - 'info', - 'debug', - 'warn', - 'error' - ].map(lvl => [lvl, logLevel(lvl)])) as unknown as Console; - - const plugin = await createPlugin({wasm: [ - { url: 'http://localhost:8124/wasm/log.wasm' } - ]}, { useWasi: true, logger }); - - try { - await plugin.call('run_test', ''); - assert.deepEqual(intercept, { - debug: 'this is a debug log', - error: 'this is an erorr log', - info: 'this is an info log', - warn: 'this is a warning log' - }); - } finally { - await plugin.close(); - } -}); - -test('host functions may read info from context and return values', async () => { - let executed: any - const functions = { - env: { - hello_world (context: CallContext, off: bigint) { - executed = context.readString(off); - return context.store("wow okay then") - } + test('createPlugin fails on hash mismatch (hash mismatch)', async () => { + const [err, plugin] = await createPlugin( + { + wasm: [ + { + url: 'http://localhost:8124/wasm/code.wasm', + hash: '7def5bb4aa3843a5daf5d6078f1e8540e5ef10b035a9d9387e9bd5156d2b256a', + }, + ], + }, + { useWasi: true }, + ).then( + (data) => [null, data], + (err) => [err, null], + ); + + assert(plugin === null); + assert(/hash mismatch/.test(err.message)); + if (plugin) { + await plugin.close(); } - } - const plugin = await createPlugin({wasm: [ - { url: 'http://localhost:8124/wasm/code-functions.wasm' } - ]}, { useWasi: true, functions }); - - try { - const output = await plugin.call('count_vowels', 'hello world'); - assert.equal(new TextDecoder().decode(output as Uint8Array), "wow okay then") - assert.equal(executed, '{"count": 3}') - } finally { - await plugin.close(); - } -}) - -test('host functions reject original promise when throwing', async () => { - const expected = String(Math.random()) - const functions = { - env: { - hello_world (_context: CallContext, _off: bigint) { - throw new Error(expected) - } + }); + + test('createPlugin loads a module and provides access to exports/imports', async () => { + const plugin = await createPlugin({ wasm: [{ url: 'http://localhost:8124/wasm/code.wasm' }] }, { useWasi: true }); + + try { + const exports = await plugin.getExports(); + assert.deepEqual( + exports.map((xs) => xs.name).sort(), + ['memory', 'count_vowels', '__data_end', '__heap_base'].sort(), + ); + + const imports = await plugin.getImports(); + assert.deepEqual( + imports.map((xs) => xs.name).sort(), + [ + 'extism_alloc', + 'extism_output_set', + 'extism_input_length', + 'extism_input_load_u64', + 'extism_input_load_u8', + 'extism_store_u64', + 'extism_store_u8', + ].sort(), + ); + } finally { + await plugin.close(); } - } - const plugin = await createPlugin({wasm: [ - { url: 'http://localhost:8124/wasm/code-functions.wasm' } - ]}, { useWasi: true, functions }); - - try { - const [err, data] = await plugin.call('count_vowels', 'hello world').then( - data => [null, data], - err => [err, null] - ); + }); - assert(data === null); - assert.equal(err?.message, expected); - } finally { - await plugin.close(); - } -}) + test('createPlugin returns an interface that can call wasm functions', async () => { + const plugin = await createPlugin({ wasm: [{ url: 'http://localhost:8124/wasm/code.wasm' }] }, { useWasi: true }); -if (FEATURES.hasWorkerCapability) { - test('host functions may be async if worker is off-main-thread', async () => { - const functions = { - env: { - async hello_world (context: CallContext, _off: bigint) { - await new Promise(resolve => setTimeout(resolve, 100)); - return context.store("it works") - } - } + try { + const result = await plugin.call('count_vowels', 'hello world'); + assert(result, 'result is not null'); + + assert.deepEqual(JSON.parse(new TextDecoder().decode(result.buffer)), { count: 3 }); + } finally { + await plugin.close(); } + }); - const plugin = await createPlugin({wasm: [ - { url: 'http://localhost:8124/wasm/code-functions.wasm' } - ]}, { useWasi: true, functions, runInWorker: true }); + test('logging works as expected', async () => { + const intercept: Record = {}; + const logLevel = (level: string) => (message: string) => (intercept[level] = message); + + // FIXME: we're using non-blocking log functions here; to properly preserve behavior we + // should invoke these and wait on the host to return. + const logger = Object.fromEntries( + ['info', 'debug', 'warn', 'error'].map((lvl) => [lvl, logLevel(lvl)]), + ) as unknown as Console; + + const plugin = await createPlugin( + { wasm: [{ url: 'http://localhost:8124/wasm/log.wasm' }] }, + { useWasi: true, logger }, + ); try { - const output = await plugin.call('count_vowels', 'hello world'); - assert.equal(new TextDecoder().decode(output as Uint8Array), "it works") + await plugin.call('run_test', ''); + assert.deepEqual(intercept, { + debug: 'this is a debug log', + error: 'this is an erorr log', + info: 'this is an info log', + warn: 'this is a warning log', + }); } finally { await plugin.close(); } - }) + }); - test('host functions may not be reentrant off-main-thread', async () => { + test('host functions may read info from context and return values', async () => { + let executed: any; const functions = { env: { - async hello_world (context: CallContext, _off: bigint) { - await plugin?.call('count_vowels', 'hello world') - return context.store("it works") - } - } + hello_world(context: CallContext, off: bigint) { + executed = context.readString(off); + return context.store('wow okay then'); + }, + }, + }; + const plugin = await createPlugin( + { wasm: [{ url: 'http://localhost:8124/wasm/code-functions.wasm' }] }, + { useWasi: true, functions }, + ); + + try { + const output = await plugin.call('count_vowels', 'hello world'); + assert.equal(new TextDecoder().decode(output as Uint8Array), 'wow okay then'); + assert.equal(executed, '{"count": 3}'); + } finally { + await plugin.close(); } + }); - const plugin = await createPlugin({wasm: [ - { url: 'http://localhost:8124/wasm/code-functions.wasm' } - ]}, { useWasi: true, functions, runInWorker: true }); + test('host functions reject original promise when throwing', async () => { + const expected = String(Math.random()); + const functions = { + env: { + hello_world(_context: CallContext, _off: bigint) { + throw new Error(expected); + }, + }, + }; + const plugin = await createPlugin( + { wasm: [{ url: 'http://localhost:8124/wasm/code-functions.wasm' }] }, + { useWasi: true, functions }, + ); try { const [err, data] = await plugin.call('count_vowels', 'hello world').then( - data => [null, data], - err => [err, null] + (data) => [null, data], + (err) => [err, null], ); assert(data === null); - assert.equal(err?.message, 'plugin is not reentrant'); + assert.equal(err?.message, expected); } finally { await plugin.close(); } - }) -} + }); -test('createPlugin fails as expected when calling unknown function', async () => { - const plugin = await createPlugin('http://localhost:8124/wasm/code.wasm', { useWasi: true }); + if (FEATURES.hasWorkerCapability) { + test('host functions may be async if worker is off-main-thread', async () => { + const functions = { + env: { + async hello_world(context: CallContext, _off: bigint) { + await new Promise((resolve) => setTimeout(resolve, 100)); + return context.store('it works'); + }, + }, + }; + + const plugin = await createPlugin( + { wasm: [{ url: 'http://localhost:8124/wasm/code-functions.wasm' }] }, + { useWasi: true, functions, runInWorker: true }, + ); - try { - const [err, data] = await plugin.call('reticulate_splines', 'hello world').then( - data => [null, data], - err => [err, null] - ); + try { + const output = await plugin.call('count_vowels', 'hello world'); + assert.equal(new TextDecoder().decode(output as Uint8Array), 'it works'); + } finally { + await plugin.close(); + } + }); - assert(data === null); - assert.equal(err?.message, 'Plugin error: target "reticulate_splines" does not exist'); - } finally { - await plugin.close(); - } -}); + test('host functions may not be reentrant off-main-thread', async () => { + const functions = { + env: { + async hello_world(context: CallContext, _off: bigint) { + await plugin?.call('count_vowels', 'hello world'); + return context.store('it works'); + }, + }, + }; + + const plugin = await createPlugin( + { wasm: [{ url: 'http://localhost:8124/wasm/code-functions.wasm' }] }, + { useWasi: true, functions, runInWorker: true }, + ); + try { + const [err, data] = await plugin.call('count_vowels', 'hello world').then( + (data) => [null, data], + (err) => [err, null], + ); -test('plugin can allocate memory', async () => { - const plugin = await createPlugin('http://localhost:8124/wasm/alloc.wasm'); - try { - await plugin.call('run_test', ''); - } finally { - await plugin.close(); - } -}); - -test('plugin can fail gracefully', async () => { - const plugin = await createPlugin('http://localhost:8124/wasm/fail.wasm'); - try { - const [err, data] = await plugin.call('run_test', '').then( - data => [null, data], - err => [err, null] - ); - assert(data === null); - assert.equal(err.message, 'Plugin-originated error: Some error message'); - } finally { - await plugin.close(); + assert(data === null); + assert.equal(err?.message, 'plugin is not reentrant'); + } finally { + await plugin.close(); + } + }); } -}); -test('can initialize Haskell runtime', async () => { - const plugin = await createPlugin('http://localhost:8124/wasm/hello_haskell.wasm', { - config: { greeting: 'Howdy' }, - useWasi: true - }); + test('createPlugin fails as expected when calling unknown function', async () => { + const plugin = await createPlugin('http://localhost:8124/wasm/code.wasm', { useWasi: true }); + + try { + const [err, data] = await plugin.call('reticulate_splines', 'hello world').then( + (data) => [null, data], + (err) => [err, null], + ); - try { - let output = await plugin.call('testing', 'John'); + assert(data === null); + assert.equal(err?.message, 'Plugin error: target "reticulate_splines" does not exist'); + } finally { + await plugin.close(); + } + }); - let result = decode(output as Uint8Array); - assert.equal(result, 'Howdy, John'); + test('plugin can allocate memory', async () => { + const plugin = await createPlugin('http://localhost:8124/wasm/alloc.wasm'); + try { + await plugin.call('run_test', ''); + } finally { + await plugin.close(); + } + }); - output = await plugin.call('testing', 'Ben'); - assert(output !== null); - result = decode(output as Uint8Array); - assert.equal(result, 'Howdy, Ben'); - } finally { - await plugin.close(); - } -}); + test('plugin can fail gracefully', async () => { + const plugin = await createPlugin('http://localhost:8124/wasm/fail.wasm'); + try { + const [err, data] = await plugin.call('run_test', '').then( + (data) => [null, data], + (err) => [err, null], + ); + assert(data === null); + assert.equal(err.message, 'Plugin-originated error: Some error message'); + } finally { + await plugin.close(); + } + }); -if (FEATURES.fsAccess && FEATURES.supportsWasiPreview1) { - test('can access fs', async () => { - const plugin = await createPlugin('http://localhost:8124/wasm/fs.wasm', { - allowedPaths: { '/mnt': 'tests/data' }, - useWasi: true + test('can initialize Haskell runtime', async () => { + const plugin = await createPlugin('http://localhost:8124/wasm/hello_haskell.wasm', { + config: { greeting: 'Howdy' }, + useWasi: true, }); try { - const output = await plugin.call('run_test', ''); + let output = await plugin.call('testing', 'John'); + + let result = decode(output as Uint8Array); + assert.equal(result, 'Howdy, John'); + + output = await plugin.call('testing', 'Ben'); assert(output !== null); - const result = decode(output as Uint8Array); - assert.equal(result, 'hello world!'); + result = decode(output as Uint8Array); + assert.equal(result, 'Howdy, Ben'); } finally { await plugin.close(); } }); + + if (FEATURES.fsAccess && FEATURES.supportsWasiPreview1) { + test('can access fs', async () => { + const plugin = await createPlugin('http://localhost:8124/wasm/fs.wasm', { + allowedPaths: { '/mnt': 'tests/data' }, + useWasi: true, + }); + + try { + const output = await plugin.call('run_test', ''); + assert(output !== null); + const result = decode(output as Uint8Array); + assert.equal(result, 'hello world!'); + } finally { + await plugin.close(); + } + }); + } } function decode(buffer: Uint8Array) { diff --git a/src/mod.ts b/src/mod.ts index 0dbce45..b1aad06 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -3,26 +3,22 @@ export type { ManifestWasm, ManifestWasmUrl, ManifestWasmData, - ManifestLike as IntoManifest + ManifestLike as IntoManifest, } from './manifest.ts'; import { FEATURES } from 'js-sdk:features'; export { FEATURES } from 'js-sdk:features'; -import { - ManifestLike, - intoManifest as _intoManifest, - toWasmModuleData as _toWasmModuleData -} from './manifest.ts'; +import { ManifestLike, intoManifest as _intoManifest, toWasmModuleData as _toWasmModuleData } from './manifest.ts'; import { createForegroundPlugin as _createForegroundPlugin } from './foreground-plugin.ts'; import { createBackgroundPlugin as _createBackgroundPlugin } from './background-plugin.ts'; export { type CallContext } from './call-context.ts'; /** - * {@link ExtismPlugin} Config + * {@link Plugin} Config */ export interface PluginConfigLike { - [key: string]: string + [key: string]: string; } export type PluginConfig = { [key: string]: string }; @@ -66,43 +62,43 @@ export interface Plugin { // TODO: move these into another module... export interface InternalConfig { - logger: Console, - allowedHosts: string[], - allowedPaths: {[key: string]: string}, - functions: {[namespace: string]: {[func: string]: any}}, - fetch: typeof fetch, - wasiEnabled: boolean, - config: PluginConfig, + logger: Console; + allowedHosts: string[]; + allowedPaths: { [key: string]: string }; + functions: { [namespace: string]: { [func: string]: any } }; + fetch: typeof fetch; + wasiEnabled: boolean; + config: PluginConfig; } export interface InternalWasi { importObject(): Promise>; - initialize(instance: WebAssembly.Instance): Promise + initialize(instance: WebAssembly.Instance): Promise; } export default async function createPlugin( manifest: ManifestLike | PromiseLike, - opts: ExtismPluginOptions = {} + opts: ExtismPluginOptions = {}, ): Promise { manifest = await _intoManifest(await Promise.resolve(manifest)); - opts = {...opts}; + opts = { ...opts }; opts.useWasi ??= false; opts.functions = opts.functions || {}; opts.allowedPaths ??= {}; opts.allowedHosts ??= [].concat(opts.allowedHosts || []); opts.logger ??= console; - opts.config ??= {} + opts.config ??= {}; opts.runInWorker ??= FEATURES.hasWorkerCapability; if (opts.runInWorker && !FEATURES.hasWorkerCapability) { throw new Error( - 'Cannot enable off-thread wasm; current context is not `crossOriginIsolated` (see https://mdn.io/crossOriginIsolated)' + 'Cannot enable off-thread wasm; current context is not `crossOriginIsolated` (see https://mdn.io/crossOriginIsolated)', ); } const [names, moduleData] = await _toWasmModuleData(manifest, opts.fetch ?? fetch); - const config = {...opts.config, ...manifest.config}; + const config = { ...opts.config, ...manifest.config }; const allowedHosts = [...(manifest.allowed_hosts || []), ...(opts.allowedHosts || [])]; const allowedPaths = opts.allowedPaths || {}; const functions = opts.functions || {}; @@ -118,11 +114,7 @@ export default async function createPlugin( wasiEnabled, logger, config, - } + }; - return (opts.runInWorker ? _createBackgroundPlugin : _createForegroundPlugin)( - ic, - names, - moduleData, - ); + return (opts.runInWorker ? _createBackgroundPlugin : _createForegroundPlugin)(ic, names, moduleData); } diff --git a/src/plugin-options.ts b/src/plugin-options.ts index adfe537..31547b5 100644 --- a/src/plugin-options.ts +++ b/src/plugin-options.ts @@ -18,13 +18,16 @@ export interface ExtismPluginOptions { } export function intoPluginOptions(opts: ExtismPluginOptions): ExtismPluginOptions { - opts = {...opts}; + opts = { ...opts }; opts.useWasi ??= false; opts.functions = opts.functions || {}; opts.runtime ??= { - data: Uint8Array.from(atob(EMBEDDED_RUNTIME) as ArrayLike, (xs: string, _idx: number) => xs.codePointAt(0) as number), - hash: EMBEDDED_RUNTIME_HASH - } + data: Uint8Array.from( + atob(EMBEDDED_RUNTIME) as ArrayLike, + (xs: string, _idx: number) => xs.codePointAt(0) as number, + ), + hash: EMBEDDED_RUNTIME_HASH, + }; opts.allowedPaths ??= {}; opts.allowedHosts ??= [].concat(opts.allowedHosts || []); opts.logger ??= console; @@ -32,7 +35,7 @@ export function intoPluginOptions(opts: ExtismPluginOptions): ExtismPluginOption opts.runInWorker ??= FEATURES.hasWorkerCapability; if (opts.runInWorker && !FEATURES.hasWorkerCapability) { throw new Error( - 'Cannot enable off-thread wasm; current context is not `crossOriginIsolated` (see https://mdn.io/crossOriginIsolated)' + 'Cannot enable off-thread wasm; current context is not `crossOriginIsolated` (see https://mdn.io/crossOriginIsolated)', ); } diff --git a/src/polyfills/browser-features.ts b/src/polyfills/browser-features.ts index b06b3b9..b576b71 100644 --- a/src/polyfills/browser-features.ts +++ b/src/polyfills/browser-features.ts @@ -11,11 +11,10 @@ export const FEATURES = { fsAccess: false, - hasWorkerCapability: ( + hasWorkerCapability: typeof window !== 'undefined' - ? (window as any).crossOriginIsolated && typeof SharedArrayBuffer !== 'undefined' - : true - ), + ? (window as any).crossOriginIsolated && typeof SharedArrayBuffer !== 'undefined' + : true, supportsWasiPreview1: true, -} +}; diff --git a/src/polyfills/browser-fs.ts b/src/polyfills/browser-fs.ts index 7725a1e..539c014 100644 --- a/src/polyfills/browser-fs.ts +++ b/src/polyfills/browser-fs.ts @@ -1,3 +1,3 @@ export async function readFile(_path: string): Promise { - throw new Error('readFile not supported in this environment') + throw new Error('readFile not supported in this environment'); } diff --git a/src/polyfills/browser-wasi.ts b/src/polyfills/browser-wasi.ts index 7024a73..07db30e 100644 --- a/src/polyfills/browser-wasi.ts +++ b/src/polyfills/browser-wasi.ts @@ -1,7 +1,7 @@ import { WASI, Fd, File, OpenFile } from '@bjorn3/browser_wasi_shim'; import { type InternalWasi } from '../mod.ts'; -export async function loadWasi(_allowedPaths: {[from: string]: string}): Promise { +export async function loadWasi(_allowedPaths: { [from: string]: string }): Promise { const args: Array = []; const envVars: Array = []; const fds: Fd[] = [ @@ -14,7 +14,7 @@ export async function loadWasi(_allowedPaths: {[from: string]: string}): Promise return { async importObject() { - return context.wasiImport + return context.wasiImport; }, async initialize(instance: WebAssembly.Instance) { @@ -27,9 +27,9 @@ export async function loadWasi(_allowedPaths: {[from: string]: string}): Promise context.start({ exports: { memory, - _start: () => { }, + _start: () => {}, }, }); - } - } + }, + }; } diff --git a/src/polyfills/bun-features.ts b/src/polyfills/bun-features.ts index 5a6b1a1..04c634b 100644 --- a/src/polyfills/bun-features.ts +++ b/src/polyfills/bun-features.ts @@ -15,4 +15,4 @@ export const FEATURES = { // See https://github.com/oven-sh/bun/issues/1960 supportsWasiPreview1: false, -} +}; diff --git a/src/polyfills/bun-worker-url.ts b/src/polyfills/bun-worker-url.ts index 341752a..0f2f293 100644 --- a/src/polyfills/bun-worker-url.ts +++ b/src/polyfills/bun-worker-url.ts @@ -1 +1 @@ -export const WORKER_URL = new URL("./worker.js", import.meta.url); +export const WORKER_URL = new URL('./worker.js', import.meta.url); diff --git a/src/polyfills/deno-features.ts b/src/polyfills/deno-features.ts index 1e39a69..67c80b1 100644 --- a/src/polyfills/deno-features.ts +++ b/src/polyfills/deno-features.ts @@ -14,4 +14,4 @@ export const FEATURES = { hasWorkerCapability: true, supportsWasiPreview1: true, -} +}; diff --git a/src/polyfills/deno-wasi.ts b/src/polyfills/deno-wasi.ts index c580f7e..069684b 100644 --- a/src/polyfills/deno-wasi.ts +++ b/src/polyfills/deno-wasi.ts @@ -1,7 +1,7 @@ import Context from 'https://deno.land/std@0.200.0/wasi/snapshot_preview1.ts'; import { type InternalWasi } from '../mod.ts'; -export async function loadWasi(allowedPaths: {[from: string]: string}): Promise { +export async function loadWasi(allowedPaths: { [from: string]: string }): Promise { const context = new Context({ preopens: allowedPaths, exitOnReturn: false, @@ -9,7 +9,7 @@ export async function loadWasi(allowedPaths: {[from: string]: string}): Promise< return { async importObject() { - return context.exports + return context.exports; }, async initialize(instance: WebAssembly.Instance) { @@ -22,9 +22,9 @@ export async function loadWasi(allowedPaths: {[from: string]: string}): Promise< context.start({ exports: { memory, - _start: () => { }, + _start: () => {}, }, }); - } - } + }, + }; } diff --git a/src/polyfills/host-node-worker_threads.ts b/src/polyfills/host-node-worker_threads.ts index 7c7f65c..0e94512 100644 --- a/src/polyfills/host-node-worker_threads.ts +++ b/src/polyfills/host-node-worker_threads.ts @@ -1,13 +1,13 @@ // This is a polyfill for the main thread in a browser context. // We're making the native Worker API look like node's worker_threads // implementation. -export const parentPort = null; +export const parentPort = null; const HANDLER_MAP = new WeakMap(); export class Worker extends (global.Worker || Object) { constructor(url: string) { - super(url, { type: 'module', credentials: 'omit', name: 'extism-worker', crossOriginIsolated: true } as any) + super(url, { type: 'module', credentials: 'omit', name: 'extism-worker', crossOriginIsolated: true } as any); } on(ev: string, action: any) { diff --git a/src/polyfills/node-features.ts b/src/polyfills/node-features.ts index 3e6ab47..0c42f5b 100644 --- a/src/polyfills/node-features.ts +++ b/src/polyfills/node-features.ts @@ -14,4 +14,4 @@ export const FEATURES = { hasWorkerCapability: true, supportsWasiPreview1: true, -} +}; diff --git a/src/polyfills/node-wasi.ts b/src/polyfills/node-wasi.ts index 7a94404..a73b4bd 100644 --- a/src/polyfills/node-wasi.ts +++ b/src/polyfills/node-wasi.ts @@ -1,7 +1,7 @@ import { WASI } from 'wasi'; import { type InternalWasi } from '../mod.ts'; -export async function loadWasi(allowedPaths: {[from: string]: string}): Promise { +export async function loadWasi(allowedPaths: { [from: string]: string }): Promise { const context = new WASI({ version: 'preview1', preopens: allowedPaths, @@ -9,7 +9,7 @@ export async function loadWasi(allowedPaths: {[from: string]: string}): Promise< return { async importObject() { - return context.wasiImport + return context.wasiImport; }, async initialize(instance: WebAssembly.Instance) { @@ -22,9 +22,9 @@ export async function loadWasi(allowedPaths: {[from: string]: string}): Promise< context.start({ exports: { memory, - _start: () => { }, + _start: () => {}, }, }); - } - } + }, + }; } diff --git a/src/polyfills/worker-node-worker_threads.ts b/src/polyfills/worker-node-worker_threads.ts index 4aa1260..95d252d 100644 --- a/src/polyfills/worker-node-worker_threads.ts +++ b/src/polyfills/worker-node-worker_threads.ts @@ -4,13 +4,13 @@ const _parentPort = null; export const parentPort = _parentPort || { - on (ev: string, fn: unknown) { + on(ev: string, fn: unknown) { addEventListener(ev, (event: MessageEvent) => { fn(event.data); - }) + }); }, - postMessage (data, txf=[]) { + postMessage(data, txf = []) { self.postMessage(data, txf); - } -} + }, +}; diff --git a/src/runtime.ts b/src/runtime.ts deleted file mode 100644 index cf2b61a..0000000 --- a/src/runtime.ts +++ /dev/null @@ -1,3 +0,0 @@ -// This file is generated by the justfile tooling. See "Developing" in README.md. -export const EMBEDDED_RUNTIME = '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'; -export const EMBEDDED_RUNTIME_HASH = 'aab40a25b5edda3fc5c9d1de1bf17af9ce5d02cbac1bacf5bdfa495b4ab9fc39' diff --git a/src/worker-url.ts b/src/worker-url.ts index 53490a8..de56e9e 100644 --- a/src/worker-url.ts +++ b/src/worker-url.ts @@ -1,2 +1,2 @@ // This file is aliased by esbuild for commonjs, esm, and browser builds. -export const WORKER_URL = new URL("./worker.ts", import.meta.url); +export const WORKER_URL = new URL('./worker.ts', import.meta.url); diff --git a/src/worker.ts b/src/worker.ts index 9821917..01ff52e 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -8,10 +8,10 @@ import { type Plugin, type InternalConfig } from './mod.ts'; const MAX_WAIT = 5000; class Reactor { - hostFlag: Int32Array | null - sharedData: SharedArrayBuffer | null - sharedDataView: DataView | null - plugin?: Plugin + hostFlag: Int32Array | null; + sharedData: SharedArrayBuffer | null; + sharedDataView: DataView | null; + plugin?: Plugin; port: Exclude; dynamicHandlers: Map Promise>; context?: CallContext; @@ -29,12 +29,9 @@ class Reactor { this.port.postMessage({ type: 'initialized' }); this.dynamicHandlers = new Map(); - this.dynamicHandlers.set('call', async ( - transfer: any[], - name: string | [string, string], - input: number | null, - state: CallState - ) => { + this.dynamicHandlers.set( + 'call', + async (transfer: any[], name: string | [string, string], input: number | null, state: CallState) => { if (!this.context) { throw new Error('invalid state: no context available to worker reactor'); } @@ -42,26 +39,27 @@ class Reactor { this.context[IMPORT_STATE](state); const results: any = await this.plugin?.callBlock(name, input).then( - indices => [null, indices], - err => [err, null] + (indices) => [null, indices], + (err) => [err, null], ); - state = this.context[EXPORT_STATE]() - for (const [block,] of state.blocks) { + state = this.context[EXPORT_STATE](); + for (const [block] of state.blocks) { if (block) { - transfer.push(block) + transfer.push(block); } } if (results[0]) { results[0] = { originalStack: results[0]?.stack, - message: results[0]?.message - } + message: results[0]?.message, + }; } - return { results, state } - }); + return { results, state }; + }, + ); this.dynamicHandlers.set('getExports', async (_txf, name) => { return this.plugin?.getExports(name); @@ -78,90 +76,96 @@ class Reactor { async handleMessage(ev: any) { switch (ev.type) { - case 'init': return await this.handleInit(ev); - case 'invoke': return await this.handleInvoke(ev); + case 'init': + return await this.handleInit(ev); + case 'invoke': + return await this.handleInvoke(ev); } } - async handleInvoke(ev: { - handler: string, - args: any[] - }) { + async handleInvoke(ev: { handler: string; args: any[] }) { const handler = this.dynamicHandlers.get(ev.handler); if (!handler) { return this.port.postMessage({ type: 'return', - result: [`no handler registered for ${ev.handler}`, null] - }) + result: [`no handler registered for ${ev.handler}`, null], + }); } const transfer: any[] = []; - const results = await handler(transfer, ...ev.args || []).then( - ok => [null, ok], - err => [err, null] + const results = await handler(transfer, ...(ev.args || [])).then( + (ok) => [null, ok], + (err) => [err, null], ); if (results[0]) { results[0] = { originalStack: results[0]?.stack, - message: results[0]?.message - } + message: results[0]?.message, + }; } - return this.port.postMessage({ - type: 'return', - results - }, transfer) + return this.port.postMessage( + { + type: 'return', + results, + }, + transfer, + ); } - async handleInit(ev: InternalConfig & { - type: string, - names: string[], - modules: ArrayBuffer[], - sharedData: SharedArrayBuffer, - functions: {[name: string]: string[]} - }) { + async handleInit( + ev: InternalConfig & { + type: string; + names: string[]; + modules: ArrayBuffer[]; + sharedData: SharedArrayBuffer; + functions: { [name: string]: string[] }; + }, + ) { this.sharedData = ev.sharedData; this.sharedDataView = new DataView(ev.sharedData); this.hostFlag = new Int32Array(this.sharedData); - const functions = Object.fromEntries(Object.entries(ev.functions).map(([namespace, funcs]) => { - return [namespace, Object.fromEntries(funcs.map(funcName => { - return [funcName, (context: CallContext, ...args: any[]) => this.callHost(context, namespace, funcName, args)] - }))] - })) + const functions = Object.fromEntries( + Object.entries(ev.functions).map(([namespace, funcs]) => { + return [ + namespace, + Object.fromEntries( + funcs.map((funcName) => { + return [ + funcName, + (context: CallContext, ...args: any[]) => this.callHost(context, namespace, funcName, args), + ]; + }), + ), + ]; + }), + ); const { type: _, modules, functions: __, ...opts } = ev; - const logLevel = (level: string) => (message: string) => this.port.postMessage({ type: 'log', level, message }) + const logLevel = (level: string) => (message: string) => this.port.postMessage({ type: 'log', level, message }); // TODO: we're using non-blocking log functions here; to properly preserve behavior we // should invoke these and wait on the host to return. - const logger = Object.fromEntries([ - 'info', - 'debug', - 'warn', - 'error' - ].map(lvl => [lvl, logLevel(lvl)])) as unknown as Console; + const logger = Object.fromEntries( + ['info', 'debug', 'warn', 'error'].map((lvl) => [lvl, logLevel(lvl)]), + ) as unknown as Console; this.context = new CallContext(ArrayBuffer, logger, ev.config); // TODO: replace our internal fetch and logger this.plugin = await _createForegroundPlugin( - {...opts, functions, fetch, logger} as InternalConfig, + { ...opts, functions, fetch, logger } as InternalConfig, ev.names, modules, - this.context + this.context, ); this.port.postMessage({ type: 'ready' }); } - callHost( - context: CallContext, - namespace: string, - func: string, - args: (number | bigint)[] - ): number | bigint | void { + callHost(context: CallContext, namespace: string, func: string, args: (number | bigint)[]): number | bigint | void { if (!this.hostFlag) { throw new Error('attempted to call host before receiving shared array buffer'); } @@ -175,130 +179,130 @@ class Reactor { func, args, state, - }) + }); - const reader = new RingBufferReader(this.sharedData as SharedArrayBuffer) - const blocks: [ArrayBufferLike | null, number][] = [] + const reader = new RingBufferReader(this.sharedData as SharedArrayBuffer); + const blocks: [ArrayBufferLike | null, number][] = []; let retval: any; - reader.pull() + reader.pull(); do { switch (reader.readUint8()) { - // end case 0: - state.blocks = blocks; - context[IMPORT_STATE](state); - Atomics.store(this.hostFlag, 0, 0); - Atomics.notify(this.hostFlag, 0); - return retval; + state.blocks = blocks; + context[IMPORT_STATE](state); + Atomics.store(this.hostFlag, 0, 0); + Atomics.notify(this.hostFlag, 0); + return retval; // ret i64 case 1: retval = reader.readUint64(); - break; + break; // ret f64 case 2: retval = reader.readFloat64(); - break; + break; // ret void case 3: - retval = undefined - break; + retval = undefined; + break; // block - case 4: { - const index = reader.readUint32(); - const len = reader.readUint32(); - if (!len) { - blocks.push([null, index]) - } else { - const output = new Uint8Array(len) - reader.read(output) - blocks.push([output.buffer, index]) + case 4: + { + const index = reader.readUint32(); + const len = reader.readUint32(); + if (!len) { + blocks.push([null, index]); + } else { + const output = new Uint8Array(len); + reader.read(output); + blocks.push([output.buffer, index]); + } } - } break; + break; // error case 255: - break; + break; } - } while (1) + } while (1); } } const _CONTEXT = new Reactor(parentPort); class RingBufferReader { - input: SharedArrayBuffer - flag: Int32Array - inputOffset: number - scratch: ArrayBuffer - scratchView: DataView + input: SharedArrayBuffer; + flag: Int32Array; + inputOffset: number; + scratch: ArrayBuffer; + scratchView: DataView; constructor(input: SharedArrayBuffer) { - this.input = input - this.inputOffset = 4 - this.flag = new Int32Array(this.input) - this.scratch = new ArrayBuffer(8) - this.scratchView = new DataView(this.scratch) + this.input = input; + this.inputOffset = 4; + this.flag = new Int32Array(this.input); + this.scratch = new ArrayBuffer(8); + this.scratchView = new DataView(this.scratch); } get available() { - return this.flag[0] + return this.flag[0]; } pull() { if (Atomics.wait(this.flag, 0, 0, MAX_WAIT) === 'timed-out') { - throw new Error(`Worker timed out waiting for response from host after ${MAX_WAIT}ms`) + throw new Error(`Worker timed out waiting for response from host after ${MAX_WAIT}ms`); } } read(output: Uint8Array) { if (output.byteLength < this.available) { - output.set(new Uint8Array(this.input).subarray(this.inputOffset, this.inputOffset + output.byteLength)) - this.inputOffset += output.byteLength - return + output.set(new Uint8Array(this.input).subarray(this.inputOffset, this.inputOffset + output.byteLength)); + this.inputOffset += output.byteLength; + return; } - let outputOffset = 0 - let inputOffset = this.inputOffset - let extent = this.available - outputOffset + let outputOffset = 0; + let inputOffset = this.inputOffset; + let extent = this.available - outputOffset; // read ::= [outputoffset, inputoffset, extent] // firstread = [this.outputOffset, 0, this.available - this.outputOffset] do { - output.set(new Uint8Array(this.input).subarray(inputOffset, inputOffset + extent), outputOffset) - outputOffset += extent + output.set(new Uint8Array(this.input).subarray(inputOffset, inputOffset + extent), outputOffset); + outputOffset += extent; Atomics.store(this.flag, 0, 0); Atomics.notify(this.flag, 0); - this.pull() - inputOffset = 4 - extent = Math.min(this.available - inputOffset, output.byteLength - outputOffset) - } while(outputOffset !== output.byteLength) + this.pull(); + inputOffset = 4; + extent = Math.min(this.available - inputOffset, output.byteLength - outputOffset); + } while (outputOffset !== output.byteLength); - this.inputOffset = inputOffset + this.inputOffset = inputOffset; } readUint8(): number { - this.read(new Uint8Array(this.scratch).subarray(0, 1)) - return this.scratchView.getUint8(0) + this.read(new Uint8Array(this.scratch).subarray(0, 1)); + return this.scratchView.getUint8(0); } readUint32(): number { - this.read(new Uint8Array(this.scratch).subarray(0, 4)) - return this.scratchView.getUint32(0, true) + this.read(new Uint8Array(this.scratch).subarray(0, 4)); + return this.scratchView.getUint32(0, true); } readUint64(): bigint { - this.read(new Uint8Array(this.scratch)) - return this.scratchView.getBigUint64(0, true) + this.read(new Uint8Array(this.scratch)); + return this.scratchView.getBigUint64(0, true); } readFloat64(): number { - this.read(new Uint8Array(this.scratch)) - return this.scratchView.getFloat64(0, true) + this.read(new Uint8Array(this.scratch)); + return this.scratchView.getFloat64(0, true); } } - From 8cc03d30fe8d9e5c9f610d8c2c293a9081d75ac1 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Fri, 27 Oct 2023 14:23:24 -0700 Subject: [PATCH 12/25] wip: gate bun test for windows --- justfile | 1 - 1 file changed, 1 deletion(-) diff --git a/justfile b/justfile index 4161dd1..372a2b5 100644 --- a/justfile +++ b/justfile @@ -318,7 +318,6 @@ test-artifacts: node --input-type=module --no-warnings /dev/null which bun; then bun run index.js; fi - bun run index.js lint *args: eslint src tests examples $args From 8abff8305fd97b3fd31135ca5247e34a8ee8dd5c Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Fri, 27 Oct 2023 16:05:21 -0700 Subject: [PATCH 13/25] doc: improve typedocs; rename features->capabilities --- .gitignore | 3 + DEVELOPING.md | 2 +- deno.json | 2 +- justfile | 18 +- src/background-plugin.ts | 6 +- src/call-context.ts | 13 +- src/foreground-plugin.ts | 2 +- src/interfaces.ts | 229 ++++++++++++++++++ src/manifest.ts | 58 +---- src/mod.test.ts | 8 +- src/mod.ts | 162 ++++++------- src/plugin-options.ts | 44 ---- ...er-features.ts => browser-capabilities.ts} | 4 +- .../{bun-features.ts => bun-capabilities.ts} | 4 +- ...{deno-features.ts => deno-capabilities.ts} | 4 +- src/polyfills/deno-wasi.ts | 2 +- ...{node-features.ts => node-capabilities.ts} | 4 +- src/worker.ts | 2 +- types/js-sdk/index.d.ts | 12 +- 19 files changed, 360 insertions(+), 219 deletions(-) create mode 100644 src/interfaces.ts delete mode 100644 src/plugin-options.ts rename src/polyfills/{browser-features.ts => browser-capabilities.ts} (84%) rename src/polyfills/{bun-features.ts => bun-capabilities.ts} (82%) rename src/polyfills/{deno-features.ts => deno-capabilities.ts} (81%) rename src/polyfills/{node-features.ts => node-capabilities.ts} (81%) diff --git a/.gitignore b/.gitignore index 1507924..2bdeaf4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # test artifacts tests/artifacts +# generated docs +docs/ + # Logs logs *.log diff --git a/DEVELOPING.md b/DEVELOPING.md index 12dfce8..cd5c7a1 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -32,7 +32,7 @@ build_worker_browser out='worker/browser' args='[]': # <-- we accept args and an [{ "format": "esm", "alias": { - "js-sdk:features": "./src/polyfills/browser-features.ts", + "js-sdk:capabilities": "./src/polyfills/browser-capabilities.ts", "node:worker_threads": "./src/polyfills/worker-node-worker_threads.ts", "js-sdk:fs": "./src/polyfills/browser-fs.ts", "js-sdk:wasi": "./src/polyfills/browser-wasi.ts", diff --git a/deno.json b/deno.json index bd5a128..9327cbd 100644 --- a/deno.json +++ b/deno.json @@ -1,7 +1,7 @@ { "imports": { "js-sdk:worker-url": "./src/worker-url.ts", - "js-sdk:features": "./src/polyfills/deno-features.ts", + "js-sdk:capabilities": "./src/polyfills/deno-capabilities.ts", "js-sdk:wasi": "./src/polyfills/deno-wasi.ts", "js-sdk:fs": "./src/polyfills/node-fs.ts" } diff --git a/justfile b/justfile index 372a2b5..eb5d48c 100644 --- a/justfile +++ b/justfile @@ -82,9 +82,9 @@ _build out args='[]': prepare # bsd sed vs gnu sed: the former "-i" REQUIRES an argument, the latter # REQUIRES not having an argument find "dist/{{ out }}" -name '*.js' | if [ $(uname) == 'Darwin' ]; then - xargs -I{} sed -i '' -e '/^(ex|im)port/s/.ts"/.js"/g' '{}' + xargs -I{} sed -i '' -e '/^((ex|im)port|} from)/s/.ts"/.js"/g' '{}' else - xargs -I{} sed -i -e '/^(ex|im)port/s/.ts"/.js"/g' '{}' + xargs -I{} sed -i -e '/^((ex|im)port|} from)/s/.ts"/.js"/g' '{}' fi # build types (TODO: switch module target based on incoming args) @@ -119,7 +119,7 @@ build_worker_node out='worker/node' args='[]': [{ "platform": "node", "alias": { - "js-sdk:features": "./src/polyfills/node-features.ts", + "js-sdk:capabilities": "./src/polyfills/node-capabilities.ts", "js-sdk:fs": "node:fs", "js-sdk:wasi": "./src/polyfills/node-wasi.ts", } @@ -133,7 +133,7 @@ build_worker_browser out='worker/browser' args='[]': [{ "format": "esm", "alias": { - "js-sdk:features": "./src/polyfills/browser-features.ts", + "js-sdk:capabilities": "./src/polyfills/browser-capabilities.ts", "node:worker_threads": "./src/polyfills/worker-node-worker_threads.ts", "js-sdk:fs": "./src/polyfills/browser-fs.ts", "js-sdk:wasi": "./src/polyfills/browser-wasi.ts", @@ -150,7 +150,7 @@ build_node_cjs out='cjs' args='[]': "platform": "node", "minify": false, "alias": { - "js-sdk:features": "./src/polyfills/node-features.ts", + "js-sdk:capabilities": "./src/polyfills/node-capabilities.ts", "js-sdk:worker-url": "./dist/worker/node/worker-url.ts", "js-sdk:fs": "node:fs/promises", "js-sdk:wasi": "./src/polyfills/node-wasi.ts", @@ -180,7 +180,7 @@ build_node_esm out='esm' args='[]': "format": "esm", "minify": false, "alias": { - "js-sdk:features": "./src/polyfills/node-features.ts", + "js-sdk:capabilities": "./src/polyfills/node-capabilities.ts", "js-sdk:worker-url": "./dist/worker/node/worker-url.ts", "js-sdk:fs": "node:fs/promises", "js-sdk:wasi": "./src/polyfills/node-wasi.ts", @@ -200,7 +200,7 @@ build_bun out='bun' args='[]': "minify": false, "alias": { "js-sdk:worker-url": "./src/polyfills/bun-worker-url.ts", - "js-sdk:features": "./src/polyfills/bun-features.ts", + "js-sdk:capabilities": "./src/polyfills/bun-capabilities.ts", "js-sdk:fs": "node:fs/promises", "js-sdk:wasi": "./src/polyfills/node-wasi.ts", } @@ -218,7 +218,7 @@ build_browser out='browser' args='[]': "define": {"global": "window"}, "format": "esm", "alias": { - "js-sdk:features": "./src/polyfills/browser-features.ts", + "js-sdk:capabilities": "./src/polyfills/browser-capabilities.ts", "node:worker_threads": "./src/polyfills/host-node-worker_threads.ts", "js-sdk:fs": "./src/polyfills/browser-fs.ts", "js-sdk:worker-url": "./dist/worker/browser/worker-url.ts", @@ -332,7 +332,7 @@ serve-docs: docs python3 -m http.server 8000 -d docs/ watch-docs: prepare - watchexec -r -w src -w README.md just serve-docs + watchexec -r -w types -w src -w README.md just serve-docs serve port='8124' logs='true': #!/usr/bin/env node diff --git a/src/background-plugin.ts b/src/background-plugin.ts index b43ecfb..7c4961f 100644 --- a/src/background-plugin.ts +++ b/src/background-plugin.ts @@ -1,8 +1,8 @@ import { CallContext, IMPORT_STATE, EXPORT_STATE, STORE, GET_BLOCK } from './call-context.ts'; -import { type InternalConfig } from './mod.ts'; +import { type InternalConfig } from './interfaces.ts'; import { WORKER_URL } from 'js-sdk:worker-url'; import { Worker } from 'node:worker_threads'; -import { FEATURES } from 'js-sdk:features'; +import { CAPABILITIES } from 'js-sdk:capabilities'; const MAX_WAIT = 5000; @@ -149,7 +149,7 @@ class BackgroundPlugin { } let buf; - if (FEATURES.allowSharedBufferCodec) { + if (CAPABILITIES.allowSharedBufferCodec) { buf = new Uint8Array(block.buffer); } else { // This platform doesn't support encoding/decoding SharedArrayBuffers. diff --git a/src/call-context.ts b/src/call-context.ts index 3ca6427..bc2d278 100644 --- a/src/call-context.ts +++ b/src/call-context.ts @@ -1,5 +1,5 @@ -import { type PluginConfig } from './mod.ts'; -import { FEATURES } from 'js-sdk:features'; +import { type PluginConfig } from './interfaces.ts'; +import { CAPABILITIES } from 'js-sdk:capabilities'; export const BEGIN = Symbol('begin'); export const END = Symbol('end'); @@ -59,7 +59,7 @@ export class CallContext { } const buffer = - !(block.buffer instanceof ArrayBuffer) && !FEATURES.allowSharedBufferCodec + !(block.buffer instanceof ArrayBuffer) && !CAPABILITIES.allowSharedBufferCodec ? new Uint8Array(block.buffer).slice().buffer : block.buffer; @@ -74,6 +74,7 @@ export class CallContext { return Block.indexToAddress(idx); } + /** @hidden */ [ENV] = { extism_alloc: (n: bigint): bigint => { const block = this.alloc(n); @@ -265,6 +266,7 @@ export class CallContext { return index; } + /** @hidden */ [GET_BLOCK](index: number): Block { const block = this.#blocks[index]; if (!block) { @@ -273,6 +275,7 @@ export class CallContext { return block; } + /** @hidden */ [IMPORT_STATE](state: CallState, copy: boolean = false) { // eslint-disable-next-line prefer-const for (let [buf, idx] of state.blocks) { @@ -286,6 +289,7 @@ export class CallContext { this.#stack = state.stack; } + /** @hidden */ [EXPORT_STATE](): CallState { return { stack: this.#stack.slice(), @@ -305,6 +309,7 @@ export class CallContext { }; } + /** @hidden */ [STORE](input?: string | Uint8Array | number) { if (!input) { return null; @@ -331,10 +336,12 @@ export class CallContext { return input; } + /** @hidden */ [BEGIN](input: number | null) { this.#stack.push([input, null, null]); } + /** @hidden */ [END](): [number | null, number | null] { const [, outputIdx, errorIdx] = this.#stack.pop() as (number | null)[]; const outputPosition = errorIdx === null ? 1 : 0; diff --git a/src/foreground-plugin.ts b/src/foreground-plugin.ts index beea647..4b4224b 100644 --- a/src/foreground-plugin.ts +++ b/src/foreground-plugin.ts @@ -1,5 +1,5 @@ import { CallContext, GET_BLOCK, BEGIN, END, ENV, STORE } from './call-context.ts'; -import { type InternalConfig } from './mod.ts'; +import { type InternalConfig } from './interfaces.ts'; import { loadWasi } from 'js-sdk:wasi'; const DYLIBSO_ENV = 'env'; diff --git a/src/interfaces.ts b/src/interfaces.ts new file mode 100644 index 0000000..e9cb93e --- /dev/null +++ b/src/interfaces.ts @@ -0,0 +1,229 @@ +/** + * {@link Plugin} Config + */ +export interface PluginConfigLike { + [key: string]: string; +} + +export type PluginConfig = Record; + +export interface Plugin { + /** + * Check if a function exists in the WebAssembly module. + * + * @param {string | [string, string]} funcName The function's name, or a tuple of target module name and function name. + * @returns {Promise} true if the function exists, otherwise false + */ + functionExists(funcName: string | [string, string]): Promise; + close(): Promise; + + /** + * Call a specific function from the WebAssembly module with provided input. + * + * @param {string | [string, string]} funcName The name of the function to call + * @param {Uint8Array | string} input The input to pass to the function + * @returns {Promise} The result from the function call + */ + call(funcName: string | [string, string], input?: string | number | Uint8Array): Promise; + callBlock(funcName: string | [string, string], input?: number | null): Promise<[number | null, number | null]>; + getExports(name?: string): Promise; + getImports(name?: string): Promise; + getInstance(name?: string): Promise; +} + +/** + * Options for initializing an Extism plugin. + */ +export interface ExtismPluginOptions { + useWasi?: boolean | undefined; + runInWorker?: boolean | undefined; + logger?: Console; + functions?: { [key: string]: { [key: string]: any } } | undefined; + allowedPaths?: { [key: string]: string } | undefined; + allowedHosts?: string[] | undefined; + config?: PluginConfigLike | undefined; + fetch?: typeof fetch; +} + +export interface InternalConfig { + logger: Console; + allowedHosts: string[]; + allowedPaths: { [key: string]: string }; + functions: { [namespace: string]: { [func: string]: any } }; + fetch: typeof fetch; + wasiEnabled: boolean; + config: PluginConfig; +} + +export interface InternalWasi { + importObject(): Promise>; + initialize(instance: WebAssembly.Instance): Promise; +} + +/** + * Represents the raw bytes of a WASM file loaded into memory + * + * @category Manifests + */ +export interface ManifestWasmData { + data: Uint8Array; +} + +/** + * Represents a url to a WASM module + */ +export interface ManifestWasmUrl { + url: URL | string; +} + +/** + * Represents a path to a WASM module + */ +export interface ManifestWasmPath { + path: string; +} + +/** + * The WASM to load as bytes, a path, or a url + * + * @property name The name of the Wasm module. Used when disambiguating {@link Plugin#call | `Plugin#call`} targets when the + * plugin embeds multiple Wasm modules. + * + * @property hash The expected SHA-256 hash of the associated Wasm module data. {@link createPlugin} validates incoming Wasm against + * provided hashes. + */ +export type ManifestWasm = (ManifestWasmUrl | ManifestWasmData | ManifestWasmPath) & { + name?: string | undefined; + hash?: string | undefined; +}; + +/** + * The manifest which describes the {@link Plugin} code and runtime constraints. This is passed to {@link createPlugin} + * + * ```js + * let manifest = { + * wasm: [{name: 'my-wasm', url: 'http://example.com/path/to/wasm'}], + * config: { + * 'greeting': 'hello' // these variables will be available via `extism_get_var` in plugins + * } + * } + * ``` + * + * Every member of `.wasm` is expected to be an instance of {@link ManifestWasm}. + * + * @see [Extism](https://extism.org/) > [Concepts](https://extism.org/docs/category/concepts) > [Manifest](https://extism.org/docs/concepts/manifest) + */ +export interface Manifest { + wasm: Array; + config?: PluginConfigLike | undefined; +} + +/** + * Any type that can be converted into an Extism {@link Manifest}. + * - `object` instances that implement {@link Manifest} are validated. + * - `ArrayBuffer` instances are converted into {@link Manifest}s with a single {@link ManifestWasmData} member. + * - `URL` instances are fetched and their responses interpreted according to their `content-type` response header. `application/wasm` and `application/octet-stream` items + * are treated as {@link ManifestWasmData} items; `application/json` and `text/json` are treated as JSON-encoded {@link Manifest}s. + * - `string` instances that start with `http://`, `https://`, or `file://` are treated as URLs. + * - `string` instances that start with `{` treated as JSON-encoded {@link Manifest}s. + * - All other `string` instances are treated as {@link ManifestWasmPath}. + * + * ```js + * let manifest = { + * wasm: [{name: 'my-wasm', url: 'http://example.com/path/to/wasm'}], + * config: { + * 'greeting': 'hello' // these variables will be available via `extism_get_var` in plugins + * } + * } + * + * let manifest = '{"wasm": {"url": "https://example.com"}}' + * let manifest = 'path/to/file.wasm' + * let manifest = new ArrayBuffer() + * ``` + * + * @see [Extism](https://extism.org/) > [Concepts](https://extism.org/docs/category/concepts) > [Manifest](https://extism.org/docs/concepts/manifest) + * + * @throws {@link TypeError} when `URL` parameters don't resolve to a known `content-type` + * @throws {@link TypeError} when the resulting {@link Manifest} does not contain a `wasm` member with valid {@link ManifestWasm} items. + */ +export type ManifestLike = Manifest | ArrayBuffer | string | URL; + +export interface Capabilities { + /** + * Whether or not the environment allows SharedArrayBuffers to be passed to `TextDecoder.decode` and `TextEncoder.encodeInto` directly + * + * - ✅ node + * - ✅ deno + * - ✅ bun + * - ❌ firefox + * - ❌ chrome + * - ❌ webkit + */ + allowSharedBufferCodec: boolean, + + /** + * Whether or not {@link ManifestWasm} items support the "path:" key. + * + * - ✅ node + * - ✅ deno + * - ✅ bun + * - ❌ firefox + * - ❌ chrome + * - ❌ webkit + */ + manifestSupportsPaths: boolean, + + /** + * Whether or not cross-origin checks are enforced for outgoing HTTP requests on this platform. + * + * - ❌ node + * - ❌ deno + * - ❌ bun + * - ✅ firefox + * - ✅ chrome + * - ✅ webkit + */ + crossOriginChecksEnforced: boolean, + + /** + * Whether or not the host environment has access to a filesystem. + * + * - ✅ node + * - ✅ deno + * - ✅ bun + * - ❌ firefox + * - ❌ chrome + * - ❌ webkit + */ + fsAccess: boolean, + + /** + * Whether or not the host environment supports moving Wasm plugin workloads to a worker. This requires + * SharedArrayBuffer support, which requires `window.crossOriginIsolated` to be true in browsers. + * + * @see [`crossOriginalIsolated` on MDN](https://mdn.io/crossOriginIsolated) + * + * - ✅ node + * - ✅ deno + * - ✅ bun + * - 🔒 firefox + * - 🔒 chrome + * - 🔒 webkit + */ + hasWorkerCapability: boolean, + + /** + * Whether or not the host environment supports WASI preview 1. + * + * @see [`WASI`](https://wasi.dev/) and [`WASI Preview 1`](https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md) + * + * - ✅ node (via [`node:wasi`](https://nodejs.org/api/wasi.html)) + * - ✅ deno (via [`deno.land/std/wasi/snapshot_preview1`](https://deno.land/std@0.200.0/wasi/snapshot_preview1.ts)) + * - ❌ bun + * - ✅ firefox (via [`@bjorn3/browser_wasi_shim`](https://www.npmjs.com/package/@bjorn3/browser_wasi_shim)) + * - ✅ chrome (via [`@bjorn3/browser_wasi_shim`](https://www.npmjs.com/package/@bjorn3/browser_wasi_shim)) + * - ✅ webkit (via [`@bjorn3/browser_wasi_shim`](https://www.npmjs.com/package/@bjorn3/browser_wasi_shim)) + */ + supportsWasiPreview1: boolean, +} + diff --git a/src/manifest.ts b/src/manifest.ts index e880905..dfcb51b 100644 --- a/src/manifest.ts +++ b/src/manifest.ts @@ -1,61 +1,6 @@ -import { PluginConfigLike } from './mod.ts'; +import type { Manifest, ManifestWasmUrl, ManifestWasmData, ManifestWasmPath, ManifestLike } from './interfaces.ts'; import { readFile } from 'js-sdk:fs'; -/** - * Represents the raw bytes of a WASM file loaded into memory - */ -export interface ManifestWasmData { - data: Uint8Array; -} - -/** - * Represents a url to a WASM module - */ -export interface ManifestWasmUrl { - url: URL | string; -} - -/** - * Represents a path to a WASM module - */ -export interface ManifestWasmPath { - path: string; -} - -/** - * The WASM to load as bytes, a path, or a url - */ -export type ManifestWasm = (ManifestWasmUrl | ManifestWasmData | ManifestWasmPath) & { - name?: string | undefined; - hash?: string | undefined; -}; - -/** - * The manifest which describes the {@link ExtismPlugin} code and - * runtime constraints. - * - * @see [Extism > Concepts > Manifest](https://extism.org/docs/concepts/manifest) - */ -export interface Manifest { - wasm: Array; - config?: PluginConfigLike | undefined; - allowed_hosts?: Array | undefined; -} - -/** - * Any type that can be converted into an Extism {@link Manifest}. - * - `object` instances that implement {@link Manifest} are validated. - * - `ArrayBuffer` instances are converted into {@link Manifest}s with a single {@link ManifestWasmData} member. - * - `URL` instances are fetched and their responses interpreted according to their `content-type` response header. `application/wasm` and `application/octet-stream` items - * are treated as {@link ManifestWasmData} items; `application/json` and `text/json` are treated as JSON-encoded {@link Manifest}s. - * - `string` instances that start with `http://`, `https://`, or `file://` are treated as URLs. - * - `string` instances that DO NOT start with `http://`, `https://`, or `file://` are treated as JSON-encoded {@link Manifest}s. - * - * @throws {@link TypeError} when `URL` parameters don't resolve to a known `content-type` - * @throws {@link TypeError} when the resulting {@link Manifest} does not contain a `wasm` member with valid {@link ManifestWasm} items. - */ -export type ManifestLike = Manifest | ArrayBuffer | string | URL; - async function _populateWasmField(candidate: ManifestLike, _fetch: typeof fetch): Promise { if (candidate instanceof ArrayBuffer) { return { wasm: [{ data: new Uint8Array(candidate as ArrayBuffer) }] }; @@ -112,7 +57,6 @@ async function _populateWasmField(candidate: ManifestLike, _fetch: typeof fetch) export async function intoManifest(candidate: ManifestLike, _fetch: typeof fetch = fetch): Promise { const manifest = (await _populateWasmField(candidate, _fetch)) as Manifest; manifest.config ??= {}; - manifest.allowed_hosts = [].concat(manifest.allowed_hosts || ([] as any)); return manifest; } diff --git a/src/mod.test.ts b/src/mod.test.ts index a6ff12a..53c464f 100644 --- a/src/mod.test.ts +++ b/src/mod.test.ts @@ -1,6 +1,6 @@ import { test } from 'node:test'; import assert from 'node:assert'; -import createPlugin, { CallContext, FEATURES } from './mod.ts'; +import createPlugin, { CallContext, CAPABILITIES } from './mod.ts'; if (typeof WebAssembly === 'undefined') { test('this platform lacks WebAssembly support', async () => { @@ -25,7 +25,7 @@ if (typeof WebAssembly === 'undefined') { } }); - if (!FEATURES.crossOriginChecksEnforced) { + if (!CAPABILITIES.crossOriginChecksEnforced) { test('can create plugin from url with hash check', async () => { const plugin = await createPlugin({ wasm: [ @@ -210,7 +210,7 @@ if (typeof WebAssembly === 'undefined') { } }); - if (FEATURES.hasWorkerCapability) { + if (CAPABILITIES.hasWorkerCapability) { test('host functions may be async if worker is off-main-thread', async () => { const functions = { env: { @@ -323,7 +323,7 @@ if (typeof WebAssembly === 'undefined') { } }); - if (FEATURES.fsAccess && FEATURES.supportsWasiPreview1) { + if (CAPABILITIES.fsAccess && CAPABILITIES.supportsWasiPreview1) { test('can access fs', async () => { const plugin = await createPlugin('http://localhost:8124/wasm/fs.wasm', { allowedPaths: { '/mnt': 'tests/data' }, diff --git a/src/mod.ts b/src/mod.ts index b1aad06..f72227a 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -1,82 +1,74 @@ -export type { - Manifest, - ManifestWasm, - ManifestWasmUrl, - ManifestWasmData, - ManifestLike as IntoManifest, -} from './manifest.ts'; -import { FEATURES } from 'js-sdk:features'; -export { FEATURES } from 'js-sdk:features'; +import { CAPABILITIES } from 'js-sdk:capabilities'; + +import type { + ManifestLike, + InternalConfig, + ExtismPluginOptions, + Plugin, +} from './interfaces.ts'; -import { ManifestLike, intoManifest as _intoManifest, toWasmModuleData as _toWasmModuleData } from './manifest.ts'; +import { intoManifest as _intoManifest, toWasmModuleData as _toWasmModuleData } from './manifest.ts'; import { createForegroundPlugin as _createForegroundPlugin } from './foreground-plugin.ts'; import { createBackgroundPlugin as _createBackgroundPlugin } from './background-plugin.ts'; -export { type CallContext } from './call-context.ts'; - -/** - * {@link Plugin} Config - */ -export interface PluginConfigLike { - [key: string]: string; -} -export type PluginConfig = { [key: string]: string }; - -/** - * Options for initializing an Extism plugin. - */ -export interface ExtismPluginOptions { - useWasi?: boolean | undefined; - runInWorker?: boolean | undefined; - logger?: Console; - functions?: { [key: string]: { [key: string]: any } } | undefined; - allowedPaths?: { [key: string]: string } | undefined; - allowedHosts?: string[] | undefined; - config?: PluginConfigLike | undefined; - fetch?: typeof fetch; -} -export interface Plugin { - /** - * Check if a function exists in the WebAssembly module. - * - * @param {string | [string, string]} funcName The function's name, or a tuple of target module name and function name. - * @returns {Promise} true if the function exists, otherwise false - */ - functionExists(funcName: string | [string, string]): Promise; - close(): Promise; +export { CAPABILITIES } from 'js-sdk:capabilities'; - /** - * Call a specific function from the WebAssembly module with provided input. - * - * @param {string | [string, string]} funcName The name of the function to call - * @param {Uint8Array | string} input The input to pass to the function - * @returns {Promise} The result from the function call - */ - call(funcName: string | [string, string], input?: string | number | Uint8Array): Promise; - callBlock(funcName: string | [string, string], input?: number | null): Promise<[number | null, number | null]>; - getExports(name?: string): Promise; - getImports(name?: string): Promise; - getInstance(name?: string): Promise; -} - -// TODO: move these into another module... -export interface InternalConfig { - logger: Console; - allowedHosts: string[]; - allowedPaths: { [key: string]: string }; - functions: { [namespace: string]: { [func: string]: any } }; - fetch: typeof fetch; - wasiEnabled: boolean; - config: PluginConfig; -} +export type { + Capabilities, + ExtismPluginOptions, + ManifestLike, + ManifestWasmData, + ManifestWasmUrl, + ManifestWasmPath, + ManifestWasm, + Manifest, + Plugin +} from './interfaces.ts'; -export interface InternalWasi { - importObject(): Promise>; - initialize(instance: WebAssembly.Instance): Promise; -} +export type { + CallContext, + CallContext as CurrentPlugin, +} from './call-context.ts' -export default async function createPlugin( +/** + * Create a {@link Plugin} given a {@link ManifestLike} and {@link ExtismPluginOptions}. + * + * Plugins wrap Wasm modules, exposing rich access to exported functions. + * + * ```ts + * const plugin = await createPlugin( + * 'https://github.com/extism/plugins/releases/download/v0.3.0/count_vowels.wasm', + * { useWasi: true } + * ); + * + * try { + * const resultBytes = await plugin.call('count_vowels', 'hello world') + * const parsed = JSON.parse(new TextDecoder().decode(resultBytes)) + * + * console.log(parsed) // { count: 3, total: 3, vowels: "aeiouAEIOU" } + * } finally { + * await plugin.close() + * } + * ``` + * + * {@link Plugin | `Plugin`} default to running on a background thread when the + * environment supports it. You can see if the current environment supports + * background plugins by checking the {@link Capabilities#hasWorkerCapability | + * `hasWorkerCapability`} property of {@link CAPABILITIES}. + * + * @param manifest A {@link ManifestLike | `ManifestLike`}. May be a `string` + * representing a URL, JSON, a path to a wasm file ({@link + * Capabilities#manifestSupportsPaths | in environments} where paths are + * supported); an [ArrayBuffer](https://mdn.io/ArrayBuffer); or a {@link + * Manifest}. + * + * @param opts {@link ExtismPluginOptions | options} for controlling the behavior + * of the plugin. + * + * @returns a promise for a {@link Plugin}. + */ +export async function createPlugin( manifest: ManifestLike | PromiseLike, opts: ExtismPluginOptions = {}, ): Promise { @@ -89,8 +81,8 @@ export default async function createPlugin( opts.logger ??= console; opts.config ??= {}; - opts.runInWorker ??= FEATURES.hasWorkerCapability; - if (opts.runInWorker && !FEATURES.hasWorkerCapability) { + opts.runInWorker ??= CAPABILITIES.hasWorkerCapability; + if (opts.runInWorker && !CAPABILITIES.hasWorkerCapability) { throw new Error( 'Cannot enable off-thread wasm; current context is not `crossOriginIsolated` (see https://mdn.io/crossOriginIsolated)', ); @@ -98,23 +90,17 @@ export default async function createPlugin( const [names, moduleData] = await _toWasmModuleData(manifest, opts.fetch ?? fetch); - const config = { ...opts.config, ...manifest.config }; - const allowedHosts = [...(manifest.allowed_hosts || []), ...(opts.allowedHosts || [])]; - const allowedPaths = opts.allowedPaths || {}; - const functions = opts.functions || {}; - const logger = opts.logger || console; - const _fetch = opts.fetch || fetch; - const wasiEnabled = Boolean(opts.useWasi); - const ic: InternalConfig = { - allowedHosts, - allowedPaths, - functions, - fetch: _fetch, - wasiEnabled, - logger, - config, + allowedHosts: opts.allowedHosts as [], + allowedPaths: opts.allowedPaths, + functions: opts.functions, + fetch: opts.fetch || fetch, + wasiEnabled: opts.useWasi, + logger: opts.logger, + config: opts.config, }; return (opts.runInWorker ? _createBackgroundPlugin : _createForegroundPlugin)(ic, names, moduleData); } + +export default createPlugin; diff --git a/src/plugin-options.ts b/src/plugin-options.ts deleted file mode 100644 index 31547b5..0000000 --- a/src/plugin-options.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { EMBEDDED_RUNTIME, EMBEDDED_RUNTIME_HASH } from './runtime.ts'; -import type { ManifestWasm } from './manifest.ts'; -import { FEATURES } from 'js-sdk:features'; - -/** - * Options for initializing an Extism plugin. - */ -export interface ExtismPluginOptions { - useWasi?: boolean | undefined; - runtime?: ManifestWasm | undefined; - runInWorker?: boolean | undefined; - logger?: Console; - functions?: { [key: string]: { [key: string]: any } } | undefined; - allowedPaths?: { [key: string]: string } | undefined; - allowedHosts?: string[] | undefined; - config?: PluginConfigLike | undefined; - fetch?: typeof fetch; -} - -export function intoPluginOptions(opts: ExtismPluginOptions): ExtismPluginOptions { - opts = { ...opts }; - opts.useWasi ??= false; - opts.functions = opts.functions || {}; - opts.runtime ??= { - data: Uint8Array.from( - atob(EMBEDDED_RUNTIME) as ArrayLike, - (xs: string, _idx: number) => xs.codePointAt(0) as number, - ), - hash: EMBEDDED_RUNTIME_HASH, - }; - opts.allowedPaths ??= {}; - opts.allowedHosts ??= [].concat(opts.allowedHosts || []); - opts.logger ??= console; - - opts.runInWorker ??= FEATURES.hasWorkerCapability; - if (opts.runInWorker && !FEATURES.hasWorkerCapability) { - throw new Error( - 'Cannot enable off-thread wasm; current context is not `crossOriginIsolated` (see https://mdn.io/crossOriginIsolated)', - ); - } - - opts.config = opts.config ?? {}; - return opts; -} diff --git a/src/polyfills/browser-features.ts b/src/polyfills/browser-capabilities.ts similarity index 84% rename from src/polyfills/browser-features.ts rename to src/polyfills/browser-capabilities.ts index b576b71..2272b8d 100644 --- a/src/polyfills/browser-features.ts +++ b/src/polyfills/browser-capabilities.ts @@ -1,4 +1,6 @@ -export const FEATURES = { +import type { Capabilities } from '../interfaces.ts'; + +export const CAPABILITIES: Capabilities = { // When false, shared buffers have to be copied to an array // buffer before passing to Text{En,De}coding() allowSharedBufferCodec: false, diff --git a/src/polyfills/bun-features.ts b/src/polyfills/bun-capabilities.ts similarity index 82% rename from src/polyfills/bun-features.ts rename to src/polyfills/bun-capabilities.ts index 04c634b..1bdc565 100644 --- a/src/polyfills/bun-features.ts +++ b/src/polyfills/bun-capabilities.ts @@ -1,4 +1,6 @@ -export const FEATURES = { +import type { Capabilities } from '../interfaces.ts'; + +export const CAPABILITIES: Capabilities = { // When false, shared buffers have to be copied to an array // buffer before passing to Text{En,De}coding() allowSharedBufferCodec: false, diff --git a/src/polyfills/deno-features.ts b/src/polyfills/deno-capabilities.ts similarity index 81% rename from src/polyfills/deno-features.ts rename to src/polyfills/deno-capabilities.ts index 67c80b1..e65b60e 100644 --- a/src/polyfills/deno-features.ts +++ b/src/polyfills/deno-capabilities.ts @@ -1,4 +1,6 @@ -export const FEATURES = { +import type { Capabilities } from '../interfaces.ts'; + +export const CAPABILITIES: Capabilities = { // When false, shared buffers have to be copied to an array // buffer before passing to Text{En,De}coding() allowSharedBufferCodec: true, diff --git a/src/polyfills/deno-wasi.ts b/src/polyfills/deno-wasi.ts index 069684b..558d37d 100644 --- a/src/polyfills/deno-wasi.ts +++ b/src/polyfills/deno-wasi.ts @@ -1,5 +1,5 @@ import Context from 'https://deno.land/std@0.200.0/wasi/snapshot_preview1.ts'; -import { type InternalWasi } from '../mod.ts'; +import { type InternalWasi } from '../interfaces.ts'; export async function loadWasi(allowedPaths: { [from: string]: string }): Promise { const context = new Context({ diff --git a/src/polyfills/node-features.ts b/src/polyfills/node-capabilities.ts similarity index 81% rename from src/polyfills/node-features.ts rename to src/polyfills/node-capabilities.ts index 0c42f5b..85ed178 100644 --- a/src/polyfills/node-features.ts +++ b/src/polyfills/node-capabilities.ts @@ -1,4 +1,6 @@ -export const FEATURES = { +import type { Capabilities } from '../interfaces.ts'; + +export const CAPABILITIES: Capabilities = { // When false, shared buffers have to be copied to an array // buffer before passing to Text{En,De}coding() allowSharedBufferCodec: false, diff --git a/src/worker.ts b/src/worker.ts index 01ff52e..f6ad195 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -2,7 +2,7 @@ import { parentPort } from 'node:worker_threads'; import { createForegroundPlugin as _createForegroundPlugin } from './foreground-plugin.ts'; import { CallContext, EXPORT_STATE, CallState, IMPORT_STATE } from './call-context.ts'; -import { type Plugin, type InternalConfig } from './mod.ts'; +import { type Plugin, type InternalConfig } from './interfaces.ts'; // TODO: make this configurable const MAX_WAIT = 5000; diff --git a/types/js-sdk/index.d.ts b/types/js-sdk/index.d.ts index fee463f..251c13a 100644 --- a/types/js-sdk/index.d.ts +++ b/types/js-sdk/index.d.ts @@ -1,5 +1,13 @@ -declare module 'js-sdk:features' { - export const FEATURES: Record; +declare module 'js-sdk:capabilities' { + import type { Capabilities } from '../../src/interfaces'; + + /** + * + * The {@link Capabilities} supported by the current platform. + * + * @see {@link Capabilities} + */ + export const CAPABILITIES: Capabilities; } declare module 'js-sdk:fs' { function readFile(path: string): Promise; From 499277a99abe837fb80365bed363a4d32ac6efe2 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Mon, 30 Oct 2023 16:00:44 -0700 Subject: [PATCH 14/25] feat: implement PluginOutput + extism var get/set --- .vscode/settings.json | 6 +- src/background-plugin.ts | 18 ++--- src/call-context.ts | 151 ++++++++++++++++++++++++++++++--------- src/foreground-plugin.ts | 13 ++-- src/interfaces.ts | 79 +++++++++++++++++++- src/mod.test.ts | 36 ++++++---- src/mod.ts | 8 +-- src/worker.ts | 6 +- 8 files changed, 238 insertions(+), 79 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 938a8f6..bfbab2b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,8 +3,8 @@ "deno.lint": true, "deno.unstable": true, "deno.enablePaths": [ - "./src/deno/mod.ts", + "./src/mod.ts", "./examples/deno.ts", - "./tests/mod.test.ts" + "./src/mod.test.ts" ] -} \ No newline at end of file +} diff --git a/src/background-plugin.ts b/src/background-plugin.ts index 7c4961f..2d21ac6 100644 --- a/src/background-plugin.ts +++ b/src/background-plugin.ts @@ -1,5 +1,5 @@ import { CallContext, IMPORT_STATE, EXPORT_STATE, STORE, GET_BLOCK } from './call-context.ts'; -import { type InternalConfig } from './interfaces.ts'; +import { PluginOutput, type InternalConfig } from './interfaces.ts'; import { WORKER_URL } from 'js-sdk:worker-url'; import { Worker } from 'node:worker_threads'; import { CAPABILITIES } from 'js-sdk:capabilities'; @@ -130,7 +130,7 @@ class BackgroundPlugin { } // host -> guest invoke() - async call(funcName: string | [string, string], input?: string | Uint8Array): Promise { + async call(funcName: string | [string, string], input?: string | Uint8Array): Promise { const index = this.#context[STORE](input); const [errorIdx, outputIdx] = await this.callBlock(funcName, index); @@ -148,15 +148,11 @@ class BackgroundPlugin { return null; } - let buf; - if (CAPABILITIES.allowSharedBufferCodec) { - buf = new Uint8Array(block.buffer); - } else { - // This platform doesn't support encoding/decoding SharedArrayBuffers. - // Copy into a non-sharedarraybuffer so that the output works with TextDecoder. - buf = new Uint8Array(block.buffer.byteLength); - buf.set(new Uint8Array(block.buffer)); - } + const buf = new PluginOutput( + CAPABILITIES.allowSharedBufferCodec + ? block.buffer + : new Uint8Array(block.buffer).slice().buffer + ); if (shouldThrow) { const msg = new TextDecoder().decode(buf); diff --git a/src/call-context.ts b/src/call-context.ts index bc2d278..2a5b8a0 100644 --- a/src/call-context.ts +++ b/src/call-context.ts @@ -1,4 +1,4 @@ -import { type PluginConfig } from './interfaces.ts'; +import { PluginOutput, type PluginConfig } from './interfaces.ts'; import { CAPABILITIES } from 'js-sdk:capabilities'; export const BEGIN = Symbol('begin'); @@ -50,8 +50,84 @@ export class CallContext { #encoder: TextEncoder; #arrayBufferType: { new (size: number): ArrayBufferLike }; #config: PluginConfig; + #vars: Map = new Map() - readString(addr: bigint | number): string | null { + /** @hidden */ + constructor( + type: { new (size: number): ArrayBufferLike }, + logger: Console, + config: PluginConfig + ) { + this.#arrayBufferType = type; + this.#logger = logger; + this.#decoder = new TextDecoder(); + this.#encoder = new TextEncoder(); + + this.#stack = []; + + // reserve the null page. + this.alloc(1); + + this.#config = config; + } + + /** + * Allocate a chunk of host memory visible to plugins via other extism host functions. + * Returns the start address of the block. + */ + alloc(size: bigint | number): bigint { + const block = new Block(new this.#arrayBufferType(Number(size)), true); + const index = this.#blocks.length; + this.#blocks.push(block); + return Block.indexToAddress(index); + } + + /** + * Read a variable from extism memory by name. + * + * @returns {@link PluginOutput} + */ + getVariable(name: string): PluginOutput | null { + if (!this.#vars.has(name)) { + return null; + } + return this.read(this.#vars.get(name) as number) + } + + /** + * Set a variable to a given string or byte array value. Returns the start + * address of the variable. The start address is reused when changing the + * value of an existing variable. + * + * @returns bigint + */ + setVariable(name: string, value: string | Uint8Array): bigint { + const newIdx = this[STORE](value); + if (newIdx === null) { + return 0n; + } + + // Re-use the old address mapping. + const oldIdx = this.#vars.get(name) ?? null; + if (oldIdx !== null) { + this.#blocks[oldIdx] = this.#blocks[newIdx]; + this.#blocks[newIdx] = null; + if (newIdx === this.#blocks.length - 1) { + this.#blocks.pop(); + } + } + + this.#vars.set(name, oldIdx ?? newIdx); + return Block.indexToAddress(oldIdx ?? newIdx); + } + + /** + * Given an address in extism memory, return a {@link PluginOutput} that represents + * a view of that memory. Returns null if the address is invalid. + * + * @returns bigint + */ + read(addr: bigint | number): PluginOutput | null { const blockIdx = Block.addressToIndex(addr); const block = this.#blocks[blockIdx]; if (!block) { @@ -63,10 +139,15 @@ export class CallContext { ? new Uint8Array(block.buffer).slice().buffer : block.buffer; - return this.#decoder.decode(buffer); + return new PluginOutput(buffer) } - store(input: string | Uint8Array | number): bigint { + /** + * Store a string or Uint8Array value in extism memory. + * + * @returns bigint + */ + store(input: string | Uint8Array): bigint { const idx = this[STORE](input); if (!idx) { throw new Error('failed to store output'); @@ -77,9 +158,7 @@ export class CallContext { /** @hidden */ [ENV] = { extism_alloc: (n: bigint): bigint => { - const block = this.alloc(n); - const addr = Block.indexToAddress(block); - return addr; + return this.alloc(n); }, extism_free: (addr: number) => { @@ -153,12 +232,14 @@ export class CallContext { }, extism_config_get: (addr: bigint): bigint => { - const key = this.readString(addr); + const item = this.read(addr); - if (key === null) { + if (item === null) { return 0n; } + const key = item.string(); + if (key in this.#config) { return this.store(this.#config[key]); } @@ -166,11 +247,32 @@ export class CallContext { return 0n; }, - extism_var_get(_i: bigint): bigint { - return 0n; + extism_var_get: (addr: bigint): bigint => { + const item = this.read(addr); + + if (item === null) { + return 0n; + } + + const key = item.string(); + return this.#vars.has(key) ? Block.indexToAddress(this.#vars.get(key) as number) : 0n; }, - extism_var_set(_n: bigint, _i: bigint) {}, + extism_var_set: (addr: bigint, valueaddr: bigint) => { + const item = this.read(addr); + + if (item === null) { + return 0n; + } + + const key = item.string(); + if (valueaddr === 0n) { + this.#vars.delete(key); + return 0n; + } + + this.#vars.set(key, Block.addressToIndex(valueaddr)); + }, extism_http_request(_requestOffset: bigint, _bodyOffset: bigint): bigint { return 0n; @@ -238,6 +340,7 @@ export class CallContext { }, }; + /** @hidden */ get #input(): Block | null { const idx = this.#stack[this.#stack.length - 1][0]; if (idx === null) { @@ -246,26 +349,6 @@ export class CallContext { return this.#blocks[idx]; } - constructor(type: { new (size: number): ArrayBufferLike }, logger: Console, config: PluginConfig) { - this.#arrayBufferType = type; - this.#logger = logger; - this.#decoder = new TextDecoder(); - this.#encoder = new TextEncoder(); - this.#stack = []; - - // reserve the null page. - this.alloc(1); - - this.#config = config; - } - - alloc(size: bigint | number): number { - const block = new Block(new this.#arrayBufferType(Number(size)), true); - const index = this.#blocks.length; - this.#blocks.push(block); - return index; - } - /** @hidden */ [GET_BLOCK](index: number): Block { const block = this.#blocks[index]; @@ -310,7 +393,7 @@ export class CallContext { } /** @hidden */ - [STORE](input?: string | Uint8Array | number) { + [STORE](input?: string | Uint8Array) { if (!input) { return null; } @@ -326,7 +409,7 @@ export class CallContext { this.#blocks.push(new Block(input.buffer, true)); return idx; } - const idx = this.alloc(input.length); + const idx = Block.addressToIndex(this.alloc(input.length)); const block = this.#blocks[idx] as Block; const buf = new Uint8Array(block.buffer); buf.set(input, 0); diff --git a/src/foreground-plugin.ts b/src/foreground-plugin.ts index 4b4224b..7bb6717 100644 --- a/src/foreground-plugin.ts +++ b/src/foreground-plugin.ts @@ -1,10 +1,10 @@ import { CallContext, GET_BLOCK, BEGIN, END, ENV, STORE } from './call-context.ts'; -import { type InternalConfig } from './interfaces.ts'; +import { PluginOutput, type InternalConfig } from './interfaces.ts'; import { loadWasi } from 'js-sdk:wasi'; const DYLIBSO_ENV = 'env'; -class ForegroundPlugin { +export class ForegroundPlugin { #context: CallContext; #modules: { guestType: string; module: WebAssembly.WebAssemblyInstantiatedSource }[]; #names: string[]; @@ -85,7 +85,7 @@ class ForegroundPlugin { } } - async call(funcName: string | [string, string], input?: string | Uint8Array): Promise { + async call(funcName: string | [string, string], input?: string | Uint8Array): Promise { const inputIdx = this.#context[STORE](input); const [errorIdx, outputIdx] = await this.callBlock(funcName, inputIdx); const shouldThrow = errorIdx !== null; @@ -100,12 +100,11 @@ class ForegroundPlugin { return null; } - const buf = new Uint8Array(block.buffer); + const output = new PluginOutput(block.buffer); if (shouldThrow) { - const msg = new TextDecoder().decode(buf); - throw new Error(`Plugin-originated error: ${msg}`); + throw new Error(`Plugin-originated error: ${output.string()}`); } - return buf; + return output; } private lookupTarget(name: any): { guestType: string; module: WebAssembly.WebAssemblyInstantiatedSource } { diff --git a/src/interfaces.ts b/src/interfaces.ts index e9cb93e..d190b5f 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -5,6 +5,80 @@ export interface PluginConfigLike { [key: string]: string; } +/** + * `PluginOutput` is a view around some memory exposed by the plugin. Typically + * returned by {@link Plugin#call | `plugin.call()`} or {@link CallContext#read + * | `callContext.read()`}. It implements the read side of + * [`DataView`](https://mdn.io/dataview) along with methods for reading string + * and JSON data out of the backing buffer. + */ +export class PluginOutput extends DataView { + static #decoder = new TextDecoder(); + #bytes: Uint8Array | null = null; + + /** @hidden */ + constructor(buffer: ArrayBufferLike) { + super(buffer) + } + + json(): any { + return JSON.parse(this.string()) + } + + arrayBuffer(): ArrayBufferLike { + return this.buffer + } + + string(): string { + return PluginOutput.#decoder.decode(this.buffer) + } + + bytes(): Uint8Array { + this.#bytes ??= new Uint8Array(this.buffer) + return this.#bytes + } + + setInt8(_byteOffset: number, _value: number): void { + throw new Error('Cannot set values on output') + } + + setInt16(_byteOffset: number, _value: number, _littleEndian?: boolean | undefined): void { + throw new Error('Cannot set values on output') + } + + setInt32(_byteOffset: number, _value: number, _littleEndian?: boolean | undefined): void { + throw new Error('Cannot set values on output') + } + + setUint8(_byteOffset: number, _value: number): void { + throw new Error('Cannot set values on output') + } + + setUint16(_byteOffset: number, _value: number, _littleEndian?: boolean | undefined): void { + throw new Error('Cannot set values on output') + } + + setUint32(_byteOffset: number, _value: number, _littleEndian?: boolean | undefined): void { + throw new Error('Cannot set values on output') + } + + setFloat32(_byteOffset: number, _value: number, _littleEndian?: boolean | undefined): void { + throw new Error('Cannot set values on output') + } + + setFloat64(_byteOffset: number, _value: number, _littleEndian?: boolean | undefined): void { + throw new Error('Cannot set values on output') + } + + setBigInt64(_byteOffset: number, _value: bigint, _littleEndian?: boolean | undefined): void { + throw new Error('Cannot set values on output') + } + + setBigUint64(_byteOffset: number, _value: bigint, _littleEndian?: boolean | undefined): void { + throw new Error('Cannot set values on output') + } +} + export type PluginConfig = Record; export interface Plugin { @@ -22,10 +96,9 @@ export interface Plugin { * * @param {string | [string, string]} funcName The name of the function to call * @param {Uint8Array | string} input The input to pass to the function - * @returns {Promise} The result from the function call + * @returns {Promise} The result from the function call */ - call(funcName: string | [string, string], input?: string | number | Uint8Array): Promise; - callBlock(funcName: string | [string, string], input?: number | null): Promise<[number | null, number | null]>; + call(funcName: string | [string, string], input?: string | number | Uint8Array): Promise; getExports(name?: string): Promise; getImports(name?: string): Promise; getInstance(name?: string): Promise; diff --git a/src/mod.test.ts b/src/mod.test.ts index 53c464f..c06153c 100644 --- a/src/mod.test.ts +++ b/src/mod.test.ts @@ -30,7 +30,7 @@ if (typeof WebAssembly === 'undefined') { const plugin = await createPlugin({ wasm: [ { - url: 'https://raw.githubusercontent.com/extism/extism/main/wasm/code.wasm', + url: 'https://raw.githubusercontent.com/extism/extism/v0.5.4/wasm/code.wasm', hash: '7def5bb4aa3843a5daf5d6078f1e8540e5ef10b035a9d9387e9bd5156d2b2565', }, ], @@ -164,7 +164,7 @@ if (typeof WebAssembly === 'undefined') { const functions = { env: { hello_world(context: CallContext, off: bigint) { - executed = context.readString(off); + executed = context.read(off)?.string(); return context.store('wow okay then'); }, }, @@ -176,7 +176,7 @@ if (typeof WebAssembly === 'undefined') { try { const output = await plugin.call('count_vowels', 'hello world'); - assert.equal(new TextDecoder().decode(output as Uint8Array), 'wow okay then'); + assert.equal(output?.string(), 'wow okay then'); assert.equal(executed, '{"count": 3}'); } finally { await plugin.close(); @@ -228,7 +228,7 @@ if (typeof WebAssembly === 'undefined') { try { const output = await plugin.call('count_vowels', 'hello world'); - assert.equal(new TextDecoder().decode(output as Uint8Array), 'it works'); + assert.equal(output?.string(), 'it works'); } finally { await plugin.close(); } @@ -302,6 +302,21 @@ if (typeof WebAssembly === 'undefined') { } }); + test('plugin can get/set variables', async () => { + const plugin = await createPlugin('http://localhost:8124/wasm/var.wasm', { useWasi: true }); + try { + const [err, data] = await plugin.call('run_test').then( + (data) => [null, data], + (err) => [err, null], + ); + + assert.equal(err, null); + assert.equal(data.string(), 'a: 200'); + } finally { + await plugin.close(); + } + }); + test('can initialize Haskell runtime', async () => { const plugin = await createPlugin('http://localhost:8124/wasm/hello_haskell.wasm', { config: { greeting: 'Howdy' }, @@ -311,13 +326,11 @@ if (typeof WebAssembly === 'undefined') { try { let output = await plugin.call('testing', 'John'); - let result = decode(output as Uint8Array); - assert.equal(result, 'Howdy, John'); + assert.equal(output?.string(), 'Howdy, John'); output = await plugin.call('testing', 'Ben'); assert(output !== null); - result = decode(output as Uint8Array); - assert.equal(result, 'Howdy, Ben'); + assert.equal(output?.string(), 'Howdy, Ben'); } finally { await plugin.close(); } @@ -333,7 +346,7 @@ if (typeof WebAssembly === 'undefined') { try { const output = await plugin.call('run_test', ''); assert(output !== null); - const result = decode(output as Uint8Array); + const result = output.string() assert.equal(result, 'hello world!'); } finally { await plugin.close(); @@ -341,8 +354,3 @@ if (typeof WebAssembly === 'undefined') { }); } } - -function decode(buffer: Uint8Array) { - const decoder = new TextDecoder(); - return decoder.decode(buffer); -} diff --git a/src/mod.ts b/src/mod.ts index f72227a..e899ffc 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -43,12 +43,12 @@ export type { * ); * * try { - * const resultBytes = await plugin.call('count_vowels', 'hello world') - * const parsed = JSON.parse(new TextDecoder().decode(resultBytes)) + * const result = await plugin.call('count_vowels', 'hello world'); + * const parsed = result.json(); * - * console.log(parsed) // { count: 3, total: 3, vowels: "aeiouAEIOU" } + * console.log(parsed); // { count: 3, total: 3, vowels: "aeiouAEIOU" } * } finally { - * await plugin.close() + * await plugin.close(); * } * ``` * diff --git a/src/worker.ts b/src/worker.ts index f6ad195..9268f4e 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -1,8 +1,8 @@ import { parentPort } from 'node:worker_threads'; -import { createForegroundPlugin as _createForegroundPlugin } from './foreground-plugin.ts'; +import { ForegroundPlugin, createForegroundPlugin as _createForegroundPlugin } from './foreground-plugin.ts'; import { CallContext, EXPORT_STATE, CallState, IMPORT_STATE } from './call-context.ts'; -import { type Plugin, type InternalConfig } from './interfaces.ts'; +import { type InternalConfig } from './interfaces.ts'; // TODO: make this configurable const MAX_WAIT = 5000; @@ -11,7 +11,7 @@ class Reactor { hostFlag: Int32Array | null; sharedData: SharedArrayBuffer | null; sharedDataView: DataView | null; - plugin?: Plugin; + plugin?: ForegroundPlugin; port: Exclude; dynamicHandlers: Map Promise>; context?: CallContext; From 452d285104fa5bf7c48c9cbff686dd715a27f016 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Mon, 30 Oct 2023 17:37:56 -0700 Subject: [PATCH 15/25] feat: implement basic http calls --- src/background-plugin.ts | 45 +++++++++++++++++++++++++++ src/call-context.ts | 8 +++-- src/foreground-plugin.ts | 2 +- src/mod.test.ts | 64 ++++++++++++++++++++++++++++++--------- src/worker.ts | 2 +- wasm/http.wasm | Bin 499605 -> 678343 bytes 6 files changed, 101 insertions(+), 20 deletions(-) diff --git a/src/background-plugin.ts b/src/background-plugin.ts index 2d21ac6..cccdbc9 100644 --- a/src/background-plugin.ts +++ b/src/background-plugin.ts @@ -3,6 +3,7 @@ import { PluginOutput, type InternalConfig } from './interfaces.ts'; import { WORKER_URL } from 'js-sdk:worker-url'; import { Worker } from 'node:worker_threads'; import { CAPABILITIES } from 'js-sdk:capabilities'; +import { DYLIBSO_ENV } from './foreground-plugin.ts'; const MAX_WAIT = 5000; @@ -273,6 +274,48 @@ class BackgroundPlugin { } } +class HttpContext { + fetch: typeof fetch + lastStatusCode: number + + constructor(_fetch: typeof fetch) { + this.fetch = _fetch + this.lastStatusCode = 0 + } + + contribute(functions: Record>) { + functions[DYLIBSO_ENV] ??= {}; + functions[DYLIBSO_ENV].extism_http_request = (callContext: CallContext, reqaddr: bigint, bodyaddr: bigint) => this.makeRequest(callContext, reqaddr, bodyaddr) + functions[DYLIBSO_ENV].extism_http_status_code = () => this.lastStatusCode + } + + async makeRequest(callContext: CallContext, reqaddr: bigint, bodyaddr: bigint) { + const req = callContext.read(reqaddr); + if (req === null) { + return 0n; + } + + + const { header, url, method } = req.json(); + const body = ( + bodyaddr === 0n || method === 'GET' || method === 'HEAD' + ? null + : callContext.read(bodyaddr)?.bytes() + ) + + const response = await this.fetch(url, { + headers: header, + method, + ...(body ? { body: body.slice() } : {}) + }) + + this.lastStatusCode = response.status + const result = callContext.store(new Uint8Array(await response.arrayBuffer())); + + return result + } +} + export async function createBackgroundPlugin( opts: InternalConfig, names: string[], @@ -280,6 +323,8 @@ export async function createBackgroundPlugin( ): Promise { const worker = new Worker(WORKER_URL); const context = new CallContext(SharedArrayBuffer, opts.logger, opts.config); + const httpContext = new HttpContext(opts.fetch); + httpContext.contribute(opts.functions) await new Promise((resolve, reject) => { worker.on('message', function handler(ev) { diff --git a/src/call-context.ts b/src/call-context.ts index 2a5b8a0..68782df 100644 --- a/src/call-context.ts +++ b/src/call-context.ts @@ -211,7 +211,7 @@ export class CallContext { const blockIdx = Block.addressToIndex(addr); const block = this.#blocks[blockIdx]; if (!block) { - throw new Error('cannot assign to this block'); + throw new Error(`cannot assign to this block (addr=${addr.toString(16).padStart(16, '0')}; length=${length})`); } if (length > block.buffer.byteLength) { @@ -274,11 +274,13 @@ export class CallContext { this.#vars.set(key, Block.addressToIndex(valueaddr)); }, - extism_http_request(_requestOffset: bigint, _bodyOffset: bigint): bigint { + extism_http_request: (_requestOffset: bigint, _bodyOffset: bigint): bigint => { + this.#logger.error('http_request is not enabled') return 0n; }, - extism_http_status_code(): number { + extism_http_status_code: (): number => { + this.#logger.error('http_status_code is not enabled') return 0; }, diff --git a/src/foreground-plugin.ts b/src/foreground-plugin.ts index 7bb6717..6ddddfe 100644 --- a/src/foreground-plugin.ts +++ b/src/foreground-plugin.ts @@ -2,7 +2,7 @@ import { CallContext, GET_BLOCK, BEGIN, END, ENV, STORE } from './call-context.t import { PluginOutput, type InternalConfig } from './interfaces.ts'; import { loadWasi } from 'js-sdk:wasi'; -const DYLIBSO_ENV = 'env'; +export const DYLIBSO_ENV = 'env'; export class ForegroundPlugin { #context: CallContext; diff --git a/src/mod.test.ts b/src/mod.test.ts index c06153c..93afce8 100644 --- a/src/mod.test.ts +++ b/src/mod.test.ts @@ -210,6 +210,21 @@ if (typeof WebAssembly === 'undefined') { } }); + test('plugin can get/set variables', async () => { + const plugin = await createPlugin('http://localhost:8124/wasm/var.wasm', { useWasi: true }); + try { + const [err, data] = await plugin.call('run_test').then( + (data) => [null, data], + (err) => [err, null], + ); + + assert.equal(err, null); + assert.equal(data.string(), 'a: 200'); + } finally { + await plugin.close(); + } + }); + if (CAPABILITIES.hasWorkerCapability) { test('host functions may be async if worker is off-main-thread', async () => { const functions = { @@ -261,6 +276,40 @@ if (typeof WebAssembly === 'undefined') { await plugin.close(); } }); + + if (!CAPABILITIES.crossOriginChecksEnforced) test('http works as expected', async () => { + const functions = { + env: { + async hello_world(context: CallContext, _off: bigint) { + await new Promise((resolve) => setTimeout(resolve, 100)); + return context.store('it works'); + }, + }, + }; + + const plugin = await createPlugin( + { wasm: [{ name: 'http', url: 'http://localhost:8124/wasm/http.wasm' }] }, + { useWasi: true, functions, runInWorker: true }, + ); + + try { + const [err, data] = await plugin.call('http_get').then( + (data) => [null, data], + (err) => [err, null], + ); + + assert(err === null); + assert.deepEqual(data.json(), { + "userId": 1, + "id": 1, + "title": "delectus aut autem", + "completed": false + }) + } finally { + await plugin.close(); + } + }); + } test('createPlugin fails as expected when calling unknown function', async () => { @@ -302,21 +351,6 @@ if (typeof WebAssembly === 'undefined') { } }); - test('plugin can get/set variables', async () => { - const plugin = await createPlugin('http://localhost:8124/wasm/var.wasm', { useWasi: true }); - try { - const [err, data] = await plugin.call('run_test').then( - (data) => [null, data], - (err) => [err, null], - ); - - assert.equal(err, null); - assert.equal(data.string(), 'a: 200'); - } finally { - await plugin.close(); - } - }); - test('can initialize Haskell runtime', async () => { const plugin = await createPlugin('http://localhost:8124/wasm/hello_haskell.wasm', { config: { greeting: 'Howdy' }, diff --git a/src/worker.ts b/src/worker.ts index 9268f4e..dc82bbe 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -1,6 +1,6 @@ import { parentPort } from 'node:worker_threads'; -import { ForegroundPlugin, createForegroundPlugin as _createForegroundPlugin } from './foreground-plugin.ts'; +import { DYLIBSO_ENV, ForegroundPlugin, createForegroundPlugin as _createForegroundPlugin } from './foreground-plugin.ts'; import { CallContext, EXPORT_STATE, CallState, IMPORT_STATE } from './call-context.ts'; import { type InternalConfig } from './interfaces.ts'; diff --git a/wasm/http.wasm b/wasm/http.wasm index cc66be896dd77c6bdf871f69bb55f5aeaa852603..73aee6a5c0bc51c0f17b149811590e42061fa25c 100644 GIT binary patch literal 678343 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

EF(Ftw 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%0DvN$#) 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 literal 499605 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 yOkujRa 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�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 From 4ad57e28299e1b722d008f5b6c7950a5f22ea109 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Mon, 30 Oct 2023 17:51:18 -0700 Subject: [PATCH 16/25] fix: remove old check for runtime module --- src/foreground-plugin.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/foreground-plugin.ts b/src/foreground-plugin.ts index 6ddddfe..4de09fe 100644 --- a/src/foreground-plugin.ts +++ b/src/foreground-plugin.ts @@ -155,9 +155,8 @@ export async function createForegroundPlugin( const modules = await Promise.all( sources.map(async (source, idx, all) => { - const isRuntime = idx === all.length; - const module = await WebAssembly.instantiate(source, (isRuntime ? {} : imports) as WebAssembly.Imports); - if (!isRuntime && module.instance.exports._start) { + const module = await WebAssembly.instantiate(source, imports); + if (wasi && module.instance.exports._start) { await wasi?.initialize(module.instance); } From e358ee44d03352bb73de91dd77d0879408c39058 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Mon, 30 Oct 2023 17:52:42 -0700 Subject: [PATCH 17/25] fix: lint & format --- src/background-plugin.ts | 34 ++++++++++------------- src/call-context.ts | 16 ++++------- src/foreground-plugin.ts | 2 +- src/interfaces.ts | 45 +++++++++++++++--------------- src/mod.test.ts | 60 ++++++++++++++++++++-------------------- src/mod.ts | 14 ++-------- src/worker.ts | 6 +++- 7 files changed, 81 insertions(+), 96 deletions(-) diff --git a/src/background-plugin.ts b/src/background-plugin.ts index cccdbc9..fd192ac 100644 --- a/src/background-plugin.ts +++ b/src/background-plugin.ts @@ -150,9 +150,7 @@ class BackgroundPlugin { } const buf = new PluginOutput( - CAPABILITIES.allowSharedBufferCodec - ? block.buffer - : new Uint8Array(block.buffer).slice().buffer + CAPABILITIES.allowSharedBufferCodec ? block.buffer : new Uint8Array(block.buffer).slice().buffer, ); if (shouldThrow) { @@ -275,18 +273,19 @@ class BackgroundPlugin { } class HttpContext { - fetch: typeof fetch - lastStatusCode: number + fetch: typeof fetch; + lastStatusCode: number; constructor(_fetch: typeof fetch) { - this.fetch = _fetch - this.lastStatusCode = 0 + this.fetch = _fetch; + this.lastStatusCode = 0; } contribute(functions: Record>) { functions[DYLIBSO_ENV] ??= {}; - functions[DYLIBSO_ENV].extism_http_request = (callContext: CallContext, reqaddr: bigint, bodyaddr: bigint) => this.makeRequest(callContext, reqaddr, bodyaddr) - functions[DYLIBSO_ENV].extism_http_status_code = () => this.lastStatusCode + functions[DYLIBSO_ENV].extism_http_request = (callContext: CallContext, reqaddr: bigint, bodyaddr: bigint) => + this.makeRequest(callContext, reqaddr, bodyaddr); + functions[DYLIBSO_ENV].extism_http_status_code = () => this.lastStatusCode; } async makeRequest(callContext: CallContext, reqaddr: bigint, bodyaddr: bigint) { @@ -295,24 +294,19 @@ class HttpContext { return 0n; } - const { header, url, method } = req.json(); - const body = ( - bodyaddr === 0n || method === 'GET' || method === 'HEAD' - ? null - : callContext.read(bodyaddr)?.bytes() - ) + const body = bodyaddr === 0n || method === 'GET' || method === 'HEAD' ? null : callContext.read(bodyaddr)?.bytes(); const response = await this.fetch(url, { headers: header, method, - ...(body ? { body: body.slice() } : {}) - }) + ...(body ? { body: body.slice() } : {}), + }); - this.lastStatusCode = response.status + this.lastStatusCode = response.status; const result = callContext.store(new Uint8Array(await response.arrayBuffer())); - return result + return result; } } @@ -324,7 +318,7 @@ export async function createBackgroundPlugin( const worker = new Worker(WORKER_URL); const context = new CallContext(SharedArrayBuffer, opts.logger, opts.config); const httpContext = new HttpContext(opts.fetch); - httpContext.contribute(opts.functions) + httpContext.contribute(opts.functions); await new Promise((resolve, reject) => { worker.on('message', function handler(ev) { diff --git a/src/call-context.ts b/src/call-context.ts index 68782df..35fd299 100644 --- a/src/call-context.ts +++ b/src/call-context.ts @@ -50,14 +50,10 @@ export class CallContext { #encoder: TextEncoder; #arrayBufferType: { new (size: number): ArrayBufferLike }; #config: PluginConfig; - #vars: Map = new Map() + #vars: Map = new Map(); /** @hidden */ - constructor( - type: { new (size: number): ArrayBufferLike }, - logger: Console, - config: PluginConfig - ) { + constructor(type: { new (size: number): ArrayBufferLike }, logger: Console, config: PluginConfig) { this.#arrayBufferType = type; this.#logger = logger; this.#decoder = new TextDecoder(); @@ -91,7 +87,7 @@ export class CallContext { if (!this.#vars.has(name)) { return null; } - return this.read(this.#vars.get(name) as number) + return this.read(this.#vars.get(name) as number); } /** @@ -139,7 +135,7 @@ export class CallContext { ? new Uint8Array(block.buffer).slice().buffer : block.buffer; - return new PluginOutput(buffer) + return new PluginOutput(buffer); } /** @@ -275,12 +271,12 @@ export class CallContext { }, extism_http_request: (_requestOffset: bigint, _bodyOffset: bigint): bigint => { - this.#logger.error('http_request is not enabled') + this.#logger.error('http_request is not enabled'); return 0n; }, extism_http_status_code: (): number => { - this.#logger.error('http_status_code is not enabled') + this.#logger.error('http_status_code is not enabled'); return 0; }, diff --git a/src/foreground-plugin.ts b/src/foreground-plugin.ts index 4de09fe..6c73819 100644 --- a/src/foreground-plugin.ts +++ b/src/foreground-plugin.ts @@ -154,7 +154,7 @@ export async function createForegroundPlugin( } const modules = await Promise.all( - sources.map(async (source, idx, all) => { + sources.map(async (source) => { const module = await WebAssembly.instantiate(source, imports); if (wasi && module.instance.exports._start) { await wasi?.initialize(module.instance); diff --git a/src/interfaces.ts b/src/interfaces.ts index d190b5f..11bf567 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -18,64 +18,64 @@ export class PluginOutput extends DataView { /** @hidden */ constructor(buffer: ArrayBufferLike) { - super(buffer) + super(buffer); } json(): any { - return JSON.parse(this.string()) + return JSON.parse(this.string()); } arrayBuffer(): ArrayBufferLike { - return this.buffer + return this.buffer; } string(): string { - return PluginOutput.#decoder.decode(this.buffer) + return PluginOutput.#decoder.decode(this.buffer); } bytes(): Uint8Array { - this.#bytes ??= new Uint8Array(this.buffer) - return this.#bytes + this.#bytes ??= new Uint8Array(this.buffer); + return this.#bytes; } setInt8(_byteOffset: number, _value: number): void { - throw new Error('Cannot set values on output') + throw new Error('Cannot set values on output'); } setInt16(_byteOffset: number, _value: number, _littleEndian?: boolean | undefined): void { - throw new Error('Cannot set values on output') + throw new Error('Cannot set values on output'); } setInt32(_byteOffset: number, _value: number, _littleEndian?: boolean | undefined): void { - throw new Error('Cannot set values on output') + throw new Error('Cannot set values on output'); } setUint8(_byteOffset: number, _value: number): void { - throw new Error('Cannot set values on output') + throw new Error('Cannot set values on output'); } setUint16(_byteOffset: number, _value: number, _littleEndian?: boolean | undefined): void { - throw new Error('Cannot set values on output') + throw new Error('Cannot set values on output'); } setUint32(_byteOffset: number, _value: number, _littleEndian?: boolean | undefined): void { - throw new Error('Cannot set values on output') + throw new Error('Cannot set values on output'); } setFloat32(_byteOffset: number, _value: number, _littleEndian?: boolean | undefined): void { - throw new Error('Cannot set values on output') + throw new Error('Cannot set values on output'); } setFloat64(_byteOffset: number, _value: number, _littleEndian?: boolean | undefined): void { - throw new Error('Cannot set values on output') + throw new Error('Cannot set values on output'); } setBigInt64(_byteOffset: number, _value: bigint, _littleEndian?: boolean | undefined): void { - throw new Error('Cannot set values on output') + throw new Error('Cannot set values on output'); } setBigUint64(_byteOffset: number, _value: bigint, _littleEndian?: boolean | undefined): void { - throw new Error('Cannot set values on output') + throw new Error('Cannot set values on output'); } } @@ -232,7 +232,7 @@ export interface Capabilities { * - ❌ chrome * - ❌ webkit */ - allowSharedBufferCodec: boolean, + allowSharedBufferCodec: boolean; /** * Whether or not {@link ManifestWasm} items support the "path:" key. @@ -244,7 +244,7 @@ export interface Capabilities { * - ❌ chrome * - ❌ webkit */ - manifestSupportsPaths: boolean, + manifestSupportsPaths: boolean; /** * Whether or not cross-origin checks are enforced for outgoing HTTP requests on this platform. @@ -256,7 +256,7 @@ export interface Capabilities { * - ✅ chrome * - ✅ webkit */ - crossOriginChecksEnforced: boolean, + crossOriginChecksEnforced: boolean; /** * Whether or not the host environment has access to a filesystem. @@ -268,7 +268,7 @@ export interface Capabilities { * - ❌ chrome * - ❌ webkit */ - fsAccess: boolean, + fsAccess: boolean; /** * Whether or not the host environment supports moving Wasm plugin workloads to a worker. This requires @@ -283,7 +283,7 @@ export interface Capabilities { * - 🔒 chrome * - 🔒 webkit */ - hasWorkerCapability: boolean, + hasWorkerCapability: boolean; /** * Whether or not the host environment supports WASI preview 1. @@ -297,6 +297,5 @@ export interface Capabilities { * - ✅ chrome (via [`@bjorn3/browser_wasi_shim`](https://www.npmjs.com/package/@bjorn3/browser_wasi_shim)) * - ✅ webkit (via [`@bjorn3/browser_wasi_shim`](https://www.npmjs.com/package/@bjorn3/browser_wasi_shim)) */ - supportsWasiPreview1: boolean, + supportsWasiPreview1: boolean; } - diff --git a/src/mod.test.ts b/src/mod.test.ts index 93afce8..c8b6691 100644 --- a/src/mod.test.ts +++ b/src/mod.test.ts @@ -277,39 +277,39 @@ if (typeof WebAssembly === 'undefined') { } }); - if (!CAPABILITIES.crossOriginChecksEnforced) test('http works as expected', async () => { - const functions = { - env: { - async hello_world(context: CallContext, _off: bigint) { - await new Promise((resolve) => setTimeout(resolve, 100)); - return context.store('it works'); + if (!CAPABILITIES.crossOriginChecksEnforced) + test('http works as expected', async () => { + const functions = { + env: { + async hello_world(context: CallContext, _off: bigint) { + await new Promise((resolve) => setTimeout(resolve, 100)); + return context.store('it works'); + }, }, - }, - }; - - const plugin = await createPlugin( - { wasm: [{ name: 'http', url: 'http://localhost:8124/wasm/http.wasm' }] }, - { useWasi: true, functions, runInWorker: true }, - ); + }; - try { - const [err, data] = await plugin.call('http_get').then( - (data) => [null, data], - (err) => [err, null], + const plugin = await createPlugin( + { wasm: [{ name: 'http', url: 'http://localhost:8124/wasm/http.wasm' }] }, + { useWasi: true, functions, runInWorker: true }, ); - assert(err === null); - assert.deepEqual(data.json(), { - "userId": 1, - "id": 1, - "title": "delectus aut autem", - "completed": false - }) - } finally { - await plugin.close(); - } - }); - + try { + const [err, data] = await plugin.call('http_get').then( + (data) => [null, data], + (err) => [err, null], + ); + + assert(err === null); + assert.deepEqual(data.json(), { + userId: 1, + id: 1, + title: 'delectus aut autem', + completed: false, + }); + } finally { + await plugin.close(); + } + }); } test('createPlugin fails as expected when calling unknown function', async () => { @@ -380,7 +380,7 @@ if (typeof WebAssembly === 'undefined') { try { const output = await plugin.call('run_test', ''); assert(output !== null); - const result = output.string() + const result = output.string(); assert.equal(result, 'hello world!'); } finally { await plugin.close(); diff --git a/src/mod.ts b/src/mod.ts index e899ffc..309f6a3 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -1,11 +1,6 @@ import { CAPABILITIES } from 'js-sdk:capabilities'; -import type { - ManifestLike, - InternalConfig, - ExtismPluginOptions, - Plugin, -} from './interfaces.ts'; +import type { ManifestLike, InternalConfig, ExtismPluginOptions, Plugin } from './interfaces.ts'; import { intoManifest as _intoManifest, toWasmModuleData as _toWasmModuleData } from './manifest.ts'; @@ -23,13 +18,10 @@ export type { ManifestWasmPath, ManifestWasm, Manifest, - Plugin + Plugin, } from './interfaces.ts'; -export type { - CallContext, - CallContext as CurrentPlugin, -} from './call-context.ts' +export type { CallContext, CallContext as CurrentPlugin } from './call-context.ts'; /** * Create a {@link Plugin} given a {@link ManifestLike} and {@link ExtismPluginOptions}. diff --git a/src/worker.ts b/src/worker.ts index dc82bbe..8924baa 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -1,6 +1,10 @@ import { parentPort } from 'node:worker_threads'; -import { DYLIBSO_ENV, ForegroundPlugin, createForegroundPlugin as _createForegroundPlugin } from './foreground-plugin.ts'; +import { + DYLIBSO_ENV, + ForegroundPlugin, + createForegroundPlugin as _createForegroundPlugin, +} from './foreground-plugin.ts'; import { CallContext, EXPORT_STATE, CallState, IMPORT_STATE } from './call-context.ts'; import { type InternalConfig } from './interfaces.ts'; From 78f13e2717e920f00f8d88c389f2062176a7cc80 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Wed, 1 Nov 2023 18:10:49 -0700 Subject: [PATCH 18/25] fix(test): add test for host fn calls that span many SAB pages this tests transferring data from the host back down to the guest thread where the data exceeds the SAB scratch page length. there's only one bug thus far: firefox gets caught in a Atomics.compareExchange loop. --- src/background-plugin.ts | 88 ++++++++++++++++++++++++++-------------- src/interfaces.ts | 36 +++++++++++++++- src/mod.test.ts | 30 ++++++++++++++ src/mod.ts | 1 + src/worker.ts | 70 ++++++++++++++++++++++---------- 5 files changed, 172 insertions(+), 53 deletions(-) diff --git a/src/background-plugin.ts b/src/background-plugin.ts index fd192ac..1889812 100644 --- a/src/background-plugin.ts +++ b/src/background-plugin.ts @@ -61,6 +61,8 @@ class BackgroundPlugin { this.opts = opts; this.#context = context; + this.hostFlag[0] = RingBufferWriter.SAB_BASE_OFFSET; + this.worker.on('message', (ev) => this.#handleMessage(ev)); } @@ -176,6 +178,7 @@ class BackgroundPlugin { // guest -> host invoke() async #handleInvoke(ev: any) { + const writer = new RingBufferWriter(this.sharedData); const namespace = this.opts.functions[ev.namespace]; const func = (namespace ?? {})[ev.func]; try { @@ -191,7 +194,6 @@ class BackgroundPlugin { // Writes to the ring buffer MAY return a promise if the write would wrap. // Writes that fit within the ring buffer return void. - const writer = new RingBufferWriter(this.sharedData); let promise: any; for (const [buffer, destination] of blocks) { promise = writer.writeUint8(SharedArrayBufferSection.Block); @@ -210,7 +212,7 @@ class BackgroundPlugin { } if (buffer) { - promise = writer.write(new Uint8Array(buffer)); + promise = writer.write(buffer); if (promise) { await promise; } @@ -243,7 +245,13 @@ class BackgroundPlugin { await promise; } } - ((await writer.writeUint8(SharedArrayBufferSection.End)) as any) || writer.flush(); + + promise = writer.writeUint8(SharedArrayBufferSection.End); + if (promise) { + await promise; + } else { + await writer.flush(); + } } catch (err) { this.close(); const [, reject] = this.#request as any[]; @@ -335,7 +343,7 @@ export async function createBackgroundPlugin( // the parent context because -- for whatever reason! -- chromium does // not allow the creation of shared buffers in worker contexts, but firefox // and webkit do. - const sharedData = new (SharedArrayBuffer as any)(1 << 16); + const sharedData = new (SharedArrayBuffer as any)(opts.sharedArrayBufferSize); const { fetch: _, logger: __, ...rest } = opts; const message = { @@ -369,35 +377,68 @@ class RingBufferWriter { scratch: ArrayBuffer; scratchView: DataView; outputOffset: number; + flag: Int32Array; + + static SAB_IDX = 0; + static SAB_BASE_OFFSET = 4; constructor(output: SharedArrayBuffer) { this.scratch = new ArrayBuffer(8); this.scratchView = new DataView(this.scratch); this.output = output; - this.outputOffset = 4; + this.outputOffset = RingBufferWriter.SAB_BASE_OFFSET; + this.flag = new Int32Array(this.output); + } + + async flush() { + const targetOffset = this.outputOffset; + this.outputOffset = RingBufferWriter.SAB_BASE_OFFSET; + while ( + Atomics.compareExchange(this.flag, RingBufferWriter.SAB_IDX, RingBufferWriter.SAB_BASE_OFFSET, targetOffset) !== + targetOffset + ) { + // TODO: firefox hangs here + } + Atomics.notify(this.flag, RingBufferWriter.SAB_IDX, 1); + + // wait for the thread to read the data out... + const result = AtomicsWaitAsync(this.flag, RingBufferWriter.SAB_IDX, targetOffset, MAX_WAIT); + + // XXX(chrisdickinson): this is cürsëd code. Add a setTimeout because it *looks* like some platforms + // don't spin their event loops if the only pending item is a Promise generated by Atomics.waitAsync. + let timer; + try { + timer = setInterval(() => {}, 0); + if (result.async) { + result.value = (await result.value) as any; + } + } finally { + clearInterval(timer); + } + + if (result.value === 'timed-out') { + throw new Error(`encountered timeout while flushing host function to worker memory ${this.flag[0]}`); + } } async spanningWrite(input: Uint8Array) { - let outputOffset = this.outputOffset; let inputOffset = 0; let toWrite = this.output.byteLength - this.outputOffset; - let flushedWriteCount = 1 + Math.floor((input.byteLength - toWrite) / this.output.byteLength); - const finalWrite = (input.byteLength - toWrite) % this.output.byteLength; + let flushedWriteCount = + 1 + Math.floor((input.byteLength - toWrite) / (this.output.byteLength - RingBufferWriter.SAB_BASE_OFFSET)); + const finalWrite = (input.byteLength - toWrite) % (this.output.byteLength - RingBufferWriter.SAB_BASE_OFFSET); do { - new Uint8Array(this.output).set(input.subarray(inputOffset, toWrite), outputOffset); - await this.flush(); + new Uint8Array(this.output).set(input.subarray(inputOffset, inputOffset + toWrite), this.outputOffset); + this.outputOffset += toWrite; inputOffset += toWrite; - outputOffset = 4; - toWrite = this.output.byteLength - 4; + await this.flush(); + toWrite = this.output.byteLength - RingBufferWriter.SAB_BASE_OFFSET; --flushedWriteCount; } while (flushedWriteCount != 0); if (finalWrite) { - new Uint8Array(this.output).set(input.subarray(inputOffset, finalWrite), outputOffset); - outputOffset += finalWrite; + this.write(input.subarray(inputOffset, inputOffset + finalWrite)); } - - this.outputOffset = outputOffset; } write(bytes: ArrayBufferLike): void | Promise { @@ -429,19 +470,4 @@ class RingBufferWriter { this.scratchView.setFloat64(0, value, true); return this.write(this.scratch.slice(0, 8)); } - - async flush() { - const flag = new Int32Array(this.output); - - Atomics.store(flag, 0, this.outputOffset); - Atomics.notify(flag, 0); - const result = AtomicsWaitAsync(flag, 0, this.outputOffset, MAX_WAIT); - if (result.async) { - result.value = (await result.value) as any; - } - - if (result.value === 'timed-out') { - throw new Error('encountered timeout while flushing host function to worker memory'); - } - } } diff --git a/src/interfaces.ts b/src/interfaces.ts index 11bf567..274e058 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -1,3 +1,5 @@ +import { CallContext } from './call-context.ts'; + /** * {@link Plugin} Config */ @@ -108,14 +110,45 @@ export interface Plugin { * Options for initializing an Extism plugin. */ export interface ExtismPluginOptions { + /** + * Whether or not to enable WASI preview 1. + */ useWasi?: boolean | undefined; + + /** + * Whether or not to run the Wasm module in a Worker thread. Requires + * {@link Capabilities#hasWorkerCapability | `CAPABILITIES.hasWorkerCapability`} to + * be true. + */ runInWorker?: boolean | undefined; + + /** + * A logger implementation. Must provide `info`, `debug`, `warn`, and `error` methods. + */ logger?: Console; - functions?: { [key: string]: { [key: string]: any } } | undefined; + + /** + * A map of namespaces to function names to host functions. + * + * ```js + * const functions = { + * 'my_great_namespace': { + * 'my_func': (callContext: CallContext, input: bigint) => { + * const output = callContext.read(input); + * if (output !== null) { + * console.log(output.string()); + * } + * } + * } + * } + * ``` + */ + functions?: { [key: string]: { [key: string]: (callContext: CallContext, ...args: any[]) => any } } | undefined; allowedPaths?: { [key: string]: string } | undefined; allowedHosts?: string[] | undefined; config?: PluginConfigLike | undefined; fetch?: typeof fetch; + sharedArrayBufferSize?: number; } export interface InternalConfig { @@ -126,6 +159,7 @@ export interface InternalConfig { fetch: typeof fetch; wasiEnabled: boolean; config: PluginConfig; + sharedArrayBufferSize: number; } export interface InternalWasi { diff --git a/src/mod.test.ts b/src/mod.test.ts index c8b6691..90e355e 100644 --- a/src/mod.test.ts +++ b/src/mod.test.ts @@ -249,6 +249,36 @@ if (typeof WebAssembly === 'undefined') { } }); + test('test writes that span multiple blocks (w/small buffer)', async () => { + const res = await fetch('http://localhost:8124/src/mod.test.ts'); + const result = await res.text(); + const functions = { + env: { + async hello_world(context: CallContext, _off: bigint) { + context.setVariable('hmmm okay storing a variable', 'hello world hello.'); + const res = await fetch('http://localhost:8124/src/mod.test.ts'); + const result = await res.text(); + return context.store(result); + }, + }, + }; + + const plugin = await createPlugin( + { wasm: [{ url: 'http://localhost:8124/wasm/code-functions.wasm' }] }, + { useWasi: true, functions, runInWorker: true, sharedArrayBufferSize: 1 << 6 }, + ); + + try { + const output = await plugin.call('count_vowels', 'hello world'); + assert.equal(output?.string(), result); + + const again = await plugin.call('count_vowels', 'hello world'); + assert.equal(again?.string(), result); + } finally { + await plugin.close(); + } + }); + test('host functions may not be reentrant off-main-thread', async () => { const functions = { env: { diff --git a/src/mod.ts b/src/mod.ts index 309f6a3..3926ce2 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -90,6 +90,7 @@ export async function createPlugin( wasiEnabled: opts.useWasi, logger: opts.logger, config: opts.config, + sharedArrayBufferSize: Number(opts.sharedArrayBufferSize) || 1 << 16, }; return (opts.runInWorker ? _createBackgroundPlugin : _createForegroundPlugin)(ic, names, moduleData); diff --git a/src/worker.ts b/src/worker.ts index 8924baa..3ad7ad3 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -176,7 +176,6 @@ class Reactor { const state = context[EXPORT_STATE](); - Atomics.store(this.hostFlag, 0, 0); this.port.postMessage({ type: 'invoke', namespace, @@ -188,16 +187,16 @@ class Reactor { const reader = new RingBufferReader(this.sharedData as SharedArrayBuffer); const blocks: [ArrayBufferLike | null, number][] = []; let retval: any; - reader.pull(); do { - switch (reader.readUint8()) { + const sectionType = reader.readUint8(); + switch (sectionType) { // end case 0: state.blocks = blocks; context[IMPORT_STATE](state); - Atomics.store(this.hostFlag, 0, 0); - Atomics.notify(this.hostFlag, 0); + reader.close(); + this.hostFlag[0] = RingBufferReader.SAB_BASE_OFFSET; return retval; // ret i64 @@ -230,8 +229,10 @@ class Reactor { } break; - // error - case 255: + default: + throw new Error( + `invalid section type="${sectionType}"; please open an issue (https://github.com/extism/js-sdk/issues/new?title=shared+array+buffer+bad+section+type+${sectionType}&labels=bug)`, + ); break; } } while (1); @@ -247,22 +248,46 @@ class RingBufferReader { scratch: ArrayBuffer; scratchView: DataView; + static SAB_IDX = 0; + static SAB_BASE_OFFSET = 4; + constructor(input: SharedArrayBuffer) { this.input = input; - this.inputOffset = 4; + this.inputOffset = RingBufferReader.SAB_BASE_OFFSET; this.flag = new Int32Array(this.input); this.scratch = new ArrayBuffer(8); this.scratchView = new DataView(this.scratch); + this.pull(false); } get available() { - return this.flag[0]; + return this.flag[0] - this.inputOffset; + } + + close() { + const expected = this.flag[0]; + while ( + Atomics.compareExchange(this.flag, RingBufferReader.SAB_IDX, expected, RingBufferReader.SAB_BASE_OFFSET) !== + RingBufferReader.SAB_BASE_OFFSET + ) {} + Atomics.notify(this.flag, RingBufferReader.SAB_IDX, MAX_WAIT); } - pull() { - if (Atomics.wait(this.flag, 0, 0, MAX_WAIT) === 'timed-out') { - throw new Error(`Worker timed out waiting for response from host after ${MAX_WAIT}ms`); + pull(reset: boolean = true) { + const expected = this.flag[0]; + if (reset) { + while ( + Atomics.compareExchange(this.flag, RingBufferReader.SAB_IDX, expected, RingBufferReader.SAB_BASE_OFFSET) !== + RingBufferReader.SAB_BASE_OFFSET + ) {} + Atomics.notify(this.flag, RingBufferReader.SAB_IDX, MAX_WAIT); } + // host now copies out, once it's done it writes the available bytes to the flag. + const v = Atomics.wait(this.flag, 0, RingBufferReader.SAB_BASE_OFFSET, MAX_WAIT); + if (v === 'timed-out') { + throw new Error(`Worker timed out waiting for response from host after ${MAX_WAIT}ms ${this.flag[0]}`); + } + this.inputOffset = RingBufferReader.SAB_BASE_OFFSET; } read(output: Uint8Array) { @@ -273,21 +298,24 @@ class RingBufferReader { } let outputOffset = 0; - let inputOffset = this.inputOffset; - let extent = this.available - outputOffset; + let extent = this.available; // read ::= [outputoffset, inputoffset, extent] // firstread = [this.outputOffset, 0, this.available - this.outputOffset] do { - output.set(new Uint8Array(this.input).subarray(inputOffset, inputOffset + extent), outputOffset); + output.set(new Uint8Array(this.input).subarray(this.inputOffset, this.inputOffset + extent), outputOffset); outputOffset += extent; - Atomics.store(this.flag, 0, 0); - Atomics.notify(this.flag, 0); + this.inputOffset += extent; + if (outputOffset === output.byteLength) { + break; + } + + if (this.available < 0) { + break; + } + this.pull(); - inputOffset = 4; - extent = Math.min(this.available - inputOffset, output.byteLength - outputOffset); + extent = Math.min(Math.max(this.available, 0), output.byteLength - outputOffset); } while (outputOffset !== output.byteLength); - - this.inputOffset = inputOffset; } readUint8(): number { From e0074662126209c9df1c5c0b9572a73e766c3e5e Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Thu, 2 Nov 2023 12:37:07 -0700 Subject: [PATCH 19/25] fix: return {async, value} from AtomicsWaitAsync polyfill in firefox --- src/background-plugin.ts | 17 ++++++++++++----- src/worker.ts | 8 +++++--- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/background-plugin.ts b/src/background-plugin.ts index 1889812..7698151 100644 --- a/src/background-plugin.ts +++ b/src/background-plugin.ts @@ -39,7 +39,7 @@ const AtomicsWaitAsync = }); }); w.postMessage([ia.buffer, index, value]); - return promise; + return { async: true, value: promise }; }; })(); @@ -249,9 +249,8 @@ class BackgroundPlugin { promise = writer.writeUint8(SharedArrayBufferSection.End); if (promise) { await promise; - } else { - await writer.flush(); } + await writer.flush(); } catch (err) { this.close(); const [, reject] = this.#request as any[]; @@ -391,21 +390,29 @@ class RingBufferWriter { } async flush() { + if (this.outputOffset === RingBufferWriter.SAB_BASE_OFFSET) { + // always make sure we flush *something* + this.writeUint8(0); + } + const targetOffset = this.outputOffset; this.outputOffset = RingBufferWriter.SAB_BASE_OFFSET; + while ( Atomics.compareExchange(this.flag, RingBufferWriter.SAB_IDX, RingBufferWriter.SAB_BASE_OFFSET, targetOffset) !== targetOffset ) { - // TODO: firefox hangs here } Atomics.notify(this.flag, RingBufferWriter.SAB_IDX, 1); // wait for the thread to read the data out... const result = AtomicsWaitAsync(this.flag, RingBufferWriter.SAB_IDX, targetOffset, MAX_WAIT); - // XXX(chrisdickinson): this is cürsëd code. Add a setTimeout because it *looks* like some platforms + // XXX(chrisdickinson): this is cürsëd code. Add a setTimeout because some platforms // don't spin their event loops if the only pending item is a Promise generated by Atomics.waitAsync. + // + // - https://github.com/nodejs/node/pull/44409 + // - https://github.com/denoland/deno/issues/14786 let timer; try { timer = setInterval(() => {}, 0); diff --git a/src/worker.ts b/src/worker.ts index 3ad7ad3..7bc66d0 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -247,6 +247,7 @@ class RingBufferReader { inputOffset: number; scratch: ArrayBuffer; scratchView: DataView; + expected: number; static SAB_IDX = 0; static SAB_BASE_OFFSET = 4; @@ -257,6 +258,7 @@ class RingBufferReader { this.flag = new Int32Array(this.input); this.scratch = new ArrayBuffer(8); this.scratchView = new DataView(this.scratch); + this.expected = 0; this.pull(false); } @@ -267,23 +269,23 @@ class RingBufferReader { close() { const expected = this.flag[0]; while ( - Atomics.compareExchange(this.flag, RingBufferReader.SAB_IDX, expected, RingBufferReader.SAB_BASE_OFFSET) !== + Atomics.compareExchange(this.flag, RingBufferReader.SAB_IDX, this.expected, RingBufferReader.SAB_BASE_OFFSET) !== RingBufferReader.SAB_BASE_OFFSET ) {} Atomics.notify(this.flag, RingBufferReader.SAB_IDX, MAX_WAIT); } pull(reset: boolean = true) { - const expected = this.flag[0]; if (reset) { while ( - Atomics.compareExchange(this.flag, RingBufferReader.SAB_IDX, expected, RingBufferReader.SAB_BASE_OFFSET) !== + Atomics.compareExchange(this.flag, RingBufferReader.SAB_IDX, this.expected, RingBufferReader.SAB_BASE_OFFSET) !== RingBufferReader.SAB_BASE_OFFSET ) {} Atomics.notify(this.flag, RingBufferReader.SAB_IDX, MAX_WAIT); } // host now copies out, once it's done it writes the available bytes to the flag. const v = Atomics.wait(this.flag, 0, RingBufferReader.SAB_BASE_OFFSET, MAX_WAIT); + this.expected = Atomics.load(this.flag, 0); if (v === 'timed-out') { throw new Error(`Worker timed out waiting for response from host after ${MAX_WAIT}ms ${this.flag[0]}`); } From 5327535421b1fa32108b80a7c967292d3586b658 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Tue, 7 Nov 2023 13:12:44 -0800 Subject: [PATCH 20/25] feat: add Plugin#reset; implement allowedHosts --- deno.json | 1 + deno.lock | 77 ++++++++++++++++++++++++++++- justfile | 4 ++ package-lock.json | 1 + package.json | 1 + src/background-plugin.ts | 42 +++++++++++++--- src/call-context.ts | 10 ++++ src/foreground-plugin.ts | 19 +++++++- src/interfaces.ts | 12 +++++ src/mod.test.ts | 85 +++++++++++++++++++++++++++++---- src/polyfills/deno-minimatch.ts | 5 ++ src/polyfills/node-minimatch.ts | 5 ++ src/worker.ts | 17 +++++-- types/js-sdk/index.d.ts | 3 ++ 14 files changed, 259 insertions(+), 23 deletions(-) create mode 100644 src/polyfills/deno-minimatch.ts create mode 100644 src/polyfills/node-minimatch.ts diff --git a/deno.json b/deno.json index 9327cbd..8e9f546 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,7 @@ { "imports": { "js-sdk:worker-url": "./src/worker-url.ts", + "js-sdk:minimatch": "./src/polyfills/deno-minimatch.ts", "js-sdk:capabilities": "./src/polyfills/deno-capabilities.ts", "js-sdk:wasi": "./src/polyfills/deno-wasi.ts", "js-sdk:fs": "./src/polyfills/node-fs.ts" diff --git a/deno.lock b/deno.lock index ed538a2..221205f 100644 --- a/deno.lock +++ b/deno.lock @@ -11,6 +11,9 @@ } } }, + "redirects": { + "https://deno.land/std/path/mod.ts": "https://deno.land/std@0.204.0/path/mod.ts" + }, "remote": { "https://deno.land/std@0.200.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", "https://deno.land/std@0.200.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", @@ -19,6 +22,78 @@ "https://deno.land/std@0.200.0/path/_util.ts": "4e191b1bac6b3bf0c31aab42e5ca2e01a86ab5a0d2e08b75acf8585047a86221", "https://deno.land/std@0.200.0/path/relative.ts": "7db80c5035016174267da16321a742d76e875215c317859a383b12f413c6f5d6", "https://deno.land/std@0.200.0/path/resolve.ts": "103b62207726a27f28177f397008545804ecb20aaf00623af1f622b18cd80b9f", - "https://deno.land/std@0.200.0/wasi/snapshot_preview1.ts": "25c18c35df4ba5755b059d3e178f3478ff210dc7fe7baef12e495cf2ed0a8b13" + "https://deno.land/std@0.200.0/wasi/snapshot_preview1.ts": "25c18c35df4ba5755b059d3e178f3478ff210dc7fe7baef12e495cf2ed0a8b13", + "https://deno.land/std@0.204.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", + "https://deno.land/std@0.204.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", + "https://deno.land/std@0.204.0/path/_common/assert_path.ts": "061e4d093d4ba5aebceb2c4da3318bfe3289e868570e9d3a8e327d91c2958946", + "https://deno.land/std@0.204.0/path/_common/basename.ts": "0d978ff818f339cd3b1d09dc914881f4d15617432ae519c1b8fdc09ff8d3789a", + "https://deno.land/std@0.204.0/path/_common/common.ts": "9e4233b2eeb50f8b2ae10ecc2108f58583aea6fd3e8907827020282dc2b76143", + "https://deno.land/std@0.204.0/path/_common/constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", + "https://deno.land/std@0.204.0/path/_common/dirname.ts": "2ba7fb4cc9fafb0f38028f434179579ce61d4d9e51296fad22b701c3d3cd7397", + "https://deno.land/std@0.204.0/path/_common/format.ts": "11aa62e316dfbf22c126917f5e03ea5fe2ee707386555a8f513d27ad5756cf96", + "https://deno.land/std@0.204.0/path/_common/from_file_url.ts": "ef1bf3197d2efbf0297a2bdbf3a61d804b18f2bcce45548ae112313ec5be3c22", + "https://deno.land/std@0.204.0/path/_common/glob_to_reg_exp.ts": "5c3c2b79fc2294ec803d102bd9855c451c150021f452046312819fbb6d4dc156", + "https://deno.land/std@0.204.0/path/_common/is_glob.ts": "567dce5c6656bdedfc6b3ee6c0833e1e4db2b8dff6e62148e94a917f289c06ad", + "https://deno.land/std@0.204.0/path/_common/normalize.ts": "2ba7fb4cc9fafb0f38028f434179579ce61d4d9e51296fad22b701c3d3cd7397", + "https://deno.land/std@0.204.0/path/_common/normalize_string.ts": "88c472f28ae49525f9fe82de8c8816d93442d46a30d6bb5063b07ff8a89ff589", + "https://deno.land/std@0.204.0/path/_common/relative.ts": "1af19d787a2a84b8c534cc487424fe101f614982ae4851382c978ab2216186b4", + "https://deno.land/std@0.204.0/path/_common/strip_trailing_separators.ts": "7ffc7c287e97bdeeee31b155828686967f222cd73f9e5780bfe7dfb1b58c6c65", + "https://deno.land/std@0.204.0/path/_common/to_file_url.ts": "a8cdd1633bc9175b7eebd3613266d7c0b6ae0fb0cff24120b6092ac31662f9ae", + "https://deno.land/std@0.204.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", + "https://deno.land/std@0.204.0/path/_os.ts": "30b0c2875f360c9296dbe6b7f2d528f0f9c741cecad2e97f803f5219e91b40a2", + "https://deno.land/std@0.204.0/path/basename.ts": "04bb5ef3e86bba8a35603b8f3b69537112cdd19ce64b77f2522006da2977a5f3", + "https://deno.land/std@0.204.0/path/common.ts": "f4d061c7d0b95a65c2a1a52439edec393e906b40f1caf4604c389fae7caa80f5", + "https://deno.land/std@0.204.0/path/dirname.ts": "88a0a71c21debafc4da7a4cd44fd32e899462df458fbca152390887d41c40361", + "https://deno.land/std@0.204.0/path/extname.ts": "2da4e2490f3b48b7121d19fb4c91681a5e11bd6bd99df4f6f47d7a71bb6ecdf2", + "https://deno.land/std@0.204.0/path/format.ts": "3457530cc85d1b4bab175f9ae73998b34fd456c830d01883169af0681b8894fb", + "https://deno.land/std@0.204.0/path/from_file_url.ts": "e7fa233ea1dff9641e8d566153a24d95010110185a6f418dd2e32320926043f8", + "https://deno.land/std@0.204.0/path/glob.ts": "9c77cf47db1d786e2ebf66670824d03fd84ecc7c807cac24441eb9d5cb6a2986", + "https://deno.land/std@0.204.0/path/is_absolute.ts": "67232b41b860571c5b7537f4954c88d86ae2ba45e883ee37d3dec27b74909d13", + "https://deno.land/std@0.204.0/path/join.ts": "98d3d76c819af4a11a81d5ba2dbb319f1ce9d63fc2b615597d4bcfddd4a89a09", + "https://deno.land/std@0.204.0/path/mod.ts": "2d62a0a8b78a60e8e6f485d881bac6b61d58573b11cf585fb7c8fc50d9b20d80", + "https://deno.land/std@0.204.0/path/normalize.ts": "aa95be9a92c7bd4f9dc0ba51e942a1973e2b93d266cd74f5ca751c136d520b66", + "https://deno.land/std@0.204.0/path/parse.ts": "d87ff0deef3fb495bc0d862278ff96da5a06acf0625ca27769fc52ac0d3d6ece", + "https://deno.land/std@0.204.0/path/posix/_util.ts": "ecf49560fedd7dd376c6156cc5565cad97c1abe9824f4417adebc7acc36c93e5", + "https://deno.land/std@0.204.0/path/posix/basename.ts": "a630aeb8fd8e27356b1823b9dedd505e30085015407caa3396332752f6b8406a", + "https://deno.land/std@0.204.0/path/posix/common.ts": "e781d395dc76f6282e3f7dd8de13194abb8b04a82d109593141abc6e95755c8b", + "https://deno.land/std@0.204.0/path/posix/dirname.ts": "f48c9c42cc670803b505478b7ef162c7cfa9d8e751b59d278b2ec59470531472", + "https://deno.land/std@0.204.0/path/posix/extname.ts": "ee7f6571a9c0a37f9218fbf510c440d1685a7c13082c348d701396cc795e0be0", + "https://deno.land/std@0.204.0/path/posix/format.ts": "b94876f77e61bfe1f147d5ccb46a920636cd3cef8be43df330f0052b03875968", + "https://deno.land/std@0.204.0/path/posix/from_file_url.ts": "b97287a83e6407ac27bdf3ab621db3fccbf1c27df0a1b1f20e1e1b5acf38a379", + "https://deno.land/std@0.204.0/path/posix/glob.ts": "86c3f06d1c98303613c74650961c3e24bdb871cde2a97c3ae7f0f6d4abbef445", + "https://deno.land/std@0.204.0/path/posix/is_absolute.ts": "159900a3422d11069d48395568217eb7fc105ceda2683d03d9b7c0f0769e01b8", + "https://deno.land/std@0.204.0/path/posix/join.ts": "0c0d84bdc344876930126640011ec1b888e6facf74153ffad9ef26813aa2a076", + "https://deno.land/std@0.204.0/path/posix/mod.ts": "6bfa8a42d85345b12dbe8571028ca2c62d460b6ef968125e498602b43b6cf6b6", + "https://deno.land/std@0.204.0/path/posix/normalize.ts": "11de90a94ab7148cc46e5a288f7d732aade1d616bc8c862f5560fa18ff987b4b", + "https://deno.land/std@0.204.0/path/posix/parse.ts": "199208f373dd93a792e9c585352bfc73a6293411bed6da6d3bc4f4ef90b04c8e", + "https://deno.land/std@0.204.0/path/posix/relative.ts": "e2f230608b0f083e6deaa06e063943e5accb3320c28aef8d87528fbb7fe6504c", + "https://deno.land/std@0.204.0/path/posix/resolve.ts": "51579d83159d5c719518c9ae50812a63959bbcb7561d79acbdb2c3682236e285", + "https://deno.land/std@0.204.0/path/posix/separator.ts": "0b6573b5f3269a3164d8edc9cefc33a02dd51003731c561008c8bb60220ebac1", + "https://deno.land/std@0.204.0/path/posix/to_file_url.ts": "08d43ea839ee75e9b8b1538376cfe95911070a655cd312bc9a00f88ef14967b6", + "https://deno.land/std@0.204.0/path/posix/to_namespaced_path.ts": "c9228a0e74fd37e76622cd7b142b8416663a9b87db643302fa0926b5a5c83bdc", + "https://deno.land/std@0.204.0/path/relative.ts": "23d45ede8b7ac464a8299663a43488aad6b561414e7cbbe4790775590db6349c", + "https://deno.land/std@0.204.0/path/resolve.ts": "5b184efc87155a0af9fa305ff68a109e28de9aee81fc3e77cd01380f19daf867", + "https://deno.land/std@0.204.0/path/separator.ts": "40a3e9a4ad10bef23bc2cd6c610291b6c502a06237c2c4cd034a15ca78dedc1f", + "https://deno.land/std@0.204.0/path/to_file_url.ts": "edaafa089e0bce386e1b2d47afe7c72e379ff93b28a5829a5885e4b6c626d864", + "https://deno.land/std@0.204.0/path/to_namespaced_path.ts": "cf8734848aac3c7527d1689d2adf82132b1618eff3cc523a775068847416b22a", + "https://deno.land/std@0.204.0/path/windows/_util.ts": "f32b9444554c8863b9b4814025c700492a2b57ff2369d015360970a1b1099d54", + "https://deno.land/std@0.204.0/path/windows/basename.ts": "8a9dbf7353d50afbc5b221af36c02a72c2d1b2b5b9f7c65bf6a5a2a0baf88ad3", + "https://deno.land/std@0.204.0/path/windows/common.ts": "e781d395dc76f6282e3f7dd8de13194abb8b04a82d109593141abc6e95755c8b", + "https://deno.land/std@0.204.0/path/windows/dirname.ts": "5c2aa541384bf0bd9aca821275d2a8690e8238fa846198ef5c7515ce31a01a94", + "https://deno.land/std@0.204.0/path/windows/extname.ts": "07f4fa1b40d06a827446b3e3bcc8d619c5546b079b8ed0c77040bbef716c7614", + "https://deno.land/std@0.204.0/path/windows/format.ts": "343019130d78f172a5c49fdc7e64686a7faf41553268961e7b6c92a6d6548edf", + "https://deno.land/std@0.204.0/path/windows/from_file_url.ts": "d53335c12b0725893d768be3ac6bf0112cc5b639d2deb0171b35988493b46199", + "https://deno.land/std@0.204.0/path/windows/glob.ts": "0286fb89ecd21db5cbf3b6c79e2b87c889b03f1311e66fb769e6b905d4142332", + "https://deno.land/std@0.204.0/path/windows/is_absolute.ts": "245b56b5f355ede8664bd7f080c910a97e2169972d23075554ae14d73722c53c", + "https://deno.land/std@0.204.0/path/windows/join.ts": "e6600bf88edeeef4e2276e155b8de1d5dec0435fd526ba2dc4d37986b2882f16", + "https://deno.land/std@0.204.0/path/windows/mod.ts": "c3d1a36fbf9f6db1320bcb4fbda8de011d25461be3497105e15cbea1e3726198", + "https://deno.land/std@0.204.0/path/windows/normalize.ts": "9deebbf40c81ef540b7b945d4ccd7a6a2c5a5992f791e6d3377043031e164e69", + "https://deno.land/std@0.204.0/path/windows/parse.ts": "120faf778fe1f22056f33ded069b68e12447668fcfa19540c0129561428d3ae5", + "https://deno.land/std@0.204.0/path/windows/relative.ts": "026855cd2c36c8f28f1df3c6fbd8f2449a2aa21f48797a74700c5d872b86d649", + "https://deno.land/std@0.204.0/path/windows/resolve.ts": "5ff441ab18a2346abadf778121128ee71bda4d0898513d4639a6ca04edca366b", + "https://deno.land/std@0.204.0/path/windows/separator.ts": "ae21f27015f10510ed1ac4a0ba9c4c9c967cbdd9d9e776a3e4967553c397bd5d", + "https://deno.land/std@0.204.0/path/windows/to_file_url.ts": "8e9ea9e1ff364aa06fa72999204229952d0a279dbb876b7b838b2b2fea55cce3", + "https://deno.land/std@0.204.0/path/windows/to_namespaced_path.ts": "e0f4d4a5e77f28a5708c1a33ff24360f35637ba6d8f103d19661255ef7bfd50d", + "https://deno.land/x/minimatch@v3.0.4/index.js": "9325ef4ec1a7e979853cb8dad980d9ee9187cacfdb378599d29b3ae9f431b46d" } } diff --git a/justfile b/justfile index eb5d48c..a51401e 100644 --- a/justfile +++ b/justfile @@ -151,6 +151,7 @@ build_node_cjs out='cjs' args='[]': "minify": false, "alias": { "js-sdk:capabilities": "./src/polyfills/node-capabilities.ts", + "js-sdk:minimatch": "./src/polyfills/node-minimatch.ts", "js-sdk:worker-url": "./dist/worker/node/worker-url.ts", "js-sdk:fs": "node:fs/promises", "js-sdk:wasi": "./src/polyfills/node-wasi.ts", @@ -181,6 +182,7 @@ build_node_esm out='esm' args='[]': "minify": false, "alias": { "js-sdk:capabilities": "./src/polyfills/node-capabilities.ts", + "js-sdk:minimatch": "./src/polyfills/node-minimatch.ts", "js-sdk:worker-url": "./dist/worker/node/worker-url.ts", "js-sdk:fs": "node:fs/promises", "js-sdk:wasi": "./src/polyfills/node-wasi.ts", @@ -200,6 +202,7 @@ build_bun out='bun' args='[]': "minify": false, "alias": { "js-sdk:worker-url": "./src/polyfills/bun-worker-url.ts", + "js-sdk:minimatch": "./src/polyfills/node-minimatch.ts", "js-sdk:capabilities": "./src/polyfills/bun-capabilities.ts", "js-sdk:fs": "node:fs/promises", "js-sdk:wasi": "./src/polyfills/node-wasi.ts", @@ -219,6 +222,7 @@ build_browser out='browser' args='[]': "format": "esm", "alias": { "js-sdk:capabilities": "./src/polyfills/browser-capabilities.ts", + "js-sdk:minimatch": "./src/polyfills/node-minimatch.ts", "node:worker_threads": "./src/polyfills/host-node-worker_threads.ts", "js-sdk:fs": "./src/polyfills/browser-fs.ts", "js-sdk:worker-url": "./dist/worker/browser/worker-url.ts", diff --git a/package-lock.json b/package-lock.json index e5f121f..5dae8d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "esbuild": "^0.15.13", "esbuild-node-builtin": "^0.1.1", "eslint": "^8.51.0", + "minimatch": "^9.0.3", "playwright": "^1.39.0", "prettier": "^2.7.1", "tape": "^5.7.1", diff --git a/package.json b/package.json index 96d970e..219bbb3 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "esbuild": "^0.15.13", "esbuild-node-builtin": "^0.1.1", "eslint": "^8.51.0", + "minimatch": "^9.0.3", "playwright": "^1.39.0", "prettier": "^2.7.1", "tape": "^5.7.1", diff --git a/src/background-plugin.ts b/src/background-plugin.ts index 7698151..0620515 100644 --- a/src/background-plugin.ts +++ b/src/background-plugin.ts @@ -1,9 +1,10 @@ -import { CallContext, IMPORT_STATE, EXPORT_STATE, STORE, GET_BLOCK } from './call-context.ts'; +import { CallContext, RESET, IMPORT_STATE, EXPORT_STATE, STORE, GET_BLOCK } from './call-context.ts'; import { PluginOutput, type InternalConfig } from './interfaces.ts'; import { WORKER_URL } from 'js-sdk:worker-url'; import { Worker } from 'node:worker_threads'; import { CAPABILITIES } from 'js-sdk:capabilities'; import { DYLIBSO_ENV } from './foreground-plugin.ts'; +import { matches } from 'js-sdk:minimatch'; const MAX_WAIT = 5000; @@ -66,6 +67,21 @@ class BackgroundPlugin { this.worker.on('message', (ev) => this.#handleMessage(ev)); } + async reset(): Promise { + if (this.isActive()) { + return false; + } + + await this.#invoke('reset'); + + this.#context[RESET](); + return true; + } + + isActive() { + return Boolean(this.#request); + } + async #handleMessage(ev: any) { switch (ev?.type) { case 'invoke': @@ -282,9 +298,11 @@ class BackgroundPlugin { class HttpContext { fetch: typeof fetch; lastStatusCode: number; + allowedHosts: string[]; - constructor(_fetch: typeof fetch) { + constructor(_fetch: typeof fetch, allowedHosts: string[]) { this.fetch = _fetch; + this.allowedHosts = allowedHosts; this.lastStatusCode = 0; } @@ -301,10 +319,21 @@ class HttpContext { return 0n; } - const { header, url, method } = req.json(); + const { header, url: rawUrl, method } = req.json(); + const url = new URL(rawUrl); + + const isAllowed = this.allowedHosts.some((allowedHost) => { + return allowedHost === url.hostname || matches(url.hostname, allowedHost); + }); + + if (!isAllowed) { + throw new Error(`Call error: HTTP request to "${url}" is not allowed (no allowedHosts match "${url.hostname}")`); + } + const body = bodyaddr === 0n || method === 'GET' || method === 'HEAD' ? null : callContext.read(bodyaddr)?.bytes(); + const fetch = this.fetch; - const response = await this.fetch(url, { + const response = await fetch(rawUrl, { headers: header, method, ...(body ? { body: body.slice() } : {}), @@ -324,7 +353,7 @@ export async function createBackgroundPlugin( ): Promise { const worker = new Worker(WORKER_URL); const context = new CallContext(SharedArrayBuffer, opts.logger, opts.config); - const httpContext = new HttpContext(opts.fetch); + const httpContext = new HttpContext(opts.fetch, opts.allowedHosts); httpContext.contribute(opts.functions); await new Promise((resolve, reject) => { @@ -401,8 +430,7 @@ class RingBufferWriter { while ( Atomics.compareExchange(this.flag, RingBufferWriter.SAB_IDX, RingBufferWriter.SAB_BASE_OFFSET, targetOffset) !== targetOffset - ) { - } + ) {} // eslint-disable-line no-empty Atomics.notify(this.flag, RingBufferWriter.SAB_IDX, 1); // wait for the thread to read the data out... diff --git a/src/call-context.ts b/src/call-context.ts index 35fd299..e87f781 100644 --- a/src/call-context.ts +++ b/src/call-context.ts @@ -8,6 +8,7 @@ export const GET_BLOCK = Symbol('get-block'); export const IMPORT_STATE = Symbol('import-state'); export const EXPORT_STATE = Symbol('export-state'); export const STORE = Symbol('store-value'); +export const RESET = Symbol('reset'); export class Block { buffer: ArrayBufferLike; @@ -347,6 +348,15 @@ export class CallContext { return this.#blocks[idx]; } + /** @hidden */ + [RESET]() { + // preserve the null page. + this.#blocks.length = 1; + + // ... but dump the stack items. + this.#stack.length = 0; + } + /** @hidden */ [GET_BLOCK](index: number): Block { const block = this.#blocks[index]; diff --git a/src/foreground-plugin.ts b/src/foreground-plugin.ts index 6c73819..05e589d 100644 --- a/src/foreground-plugin.ts +++ b/src/foreground-plugin.ts @@ -1,4 +1,4 @@ -import { CallContext, GET_BLOCK, BEGIN, END, ENV, STORE } from './call-context.ts'; +import { CallContext, RESET, GET_BLOCK, BEGIN, END, ENV, STORE } from './call-context.ts'; import { PluginOutput, type InternalConfig } from './interfaces.ts'; import { loadWasi } from 'js-sdk:wasi'; @@ -8,6 +8,7 @@ export class ForegroundPlugin { #context: CallContext; #modules: { guestType: string; module: WebAssembly.WebAssemblyInstantiatedSource }[]; #names: string[]; + #active: boolean = false; constructor( context: CallContext, @@ -19,6 +20,19 @@ export class ForegroundPlugin { this.#modules = modules; } + async reset(): Promise { + if (this.isActive()) { + return false; + } + + this.#context[RESET](); + return true; + } + + isActive() { + return this.#active; + } + async functionExists(funcName: string | [string, string]): Promise { try { const search: string[] = [].concat(funcName); @@ -53,6 +67,7 @@ export class ForegroundPlugin { } async callBlock(funcName: string | [string, string], input: number | null): Promise<[number | null, number | null]> { + this.#active = true; const search: string[] = [].concat(funcName); const [target, name] = search.length === 2 @@ -82,6 +97,8 @@ export class ForegroundPlugin { } catch (err) { this.#context[END](); throw err; + } finally { + this.#active = false; } } diff --git a/src/interfaces.ts b/src/interfaces.ts index 274e058..b214837 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -104,6 +104,18 @@ export interface Plugin { getExports(name?: string): Promise; getImports(name?: string): Promise; getInstance(name?: string): Promise; + + /** + * Whether the plugin is currently processing a call. + */ + isActive(): boolean; + + /** + * Reset Plugin memory. If called while the plugin is {@link Plugin#isActive|actively executing}, memory will not be reset. + * + * @returns {Promise} Whether or not the reset was successful. + */ + reset(): Promise; } /** diff --git a/src/mod.test.ts b/src/mod.test.ts index 90e355e..76e1dc2 100644 --- a/src/mod.test.ts +++ b/src/mod.test.ts @@ -183,6 +183,42 @@ if (typeof WebAssembly === 'undefined') { } }); + test('resetting the plugin unsets all existing pages', async () => { + const offsets: bigint[] = [0n, 0n]; + let callContext: CallContext | null = null; + + const functions = { + env: { + hello_world(context: CallContext, off: bigint) { + callContext = context; + + offsets[0] = off; + offsets[1] = context.store('wow okay then'); + return offsets[1]; + }, + }, + }; + const plugin = await createPlugin( + { wasm: [{ url: 'http://localhost:8124/wasm/code-functions.wasm' }] }, + { useWasi: true, functions }, + ); + + try { + const output = await plugin.call('count_vowels', 'hello world'); + assert.equal(output?.string(), 'wow okay then'); + + await plugin.reset(); + + assert(callContext !== null); + assert.notEqual(offsets[0], 0n); + assert.notEqual(offsets[1], 0n); + assert.equal((callContext as CallContext).read(offsets[0]), null); + assert.equal((callContext as CallContext).read(offsets[1]), null); + } finally { + await plugin.close(); + } + }); + test('host functions reject original promise when throwing', async () => { const expected = String(Math.random()); const functions = { @@ -308,7 +344,7 @@ if (typeof WebAssembly === 'undefined') { }); if (!CAPABILITIES.crossOriginChecksEnforced) - test('http works as expected', async () => { + test('http works fails as expected when no allowed hosts match', async () => { const functions = { env: { async hello_world(context: CallContext, _off: bigint) { @@ -320,7 +356,7 @@ if (typeof WebAssembly === 'undefined') { const plugin = await createPlugin( { wasm: [{ name: 'http', url: 'http://localhost:8124/wasm/http.wasm' }] }, - { useWasi: true, functions, runInWorker: true }, + { useWasi: true, functions, runInWorker: true, allowedHosts: ['*.example.com'] }, ); try { @@ -329,17 +365,48 @@ if (typeof WebAssembly === 'undefined') { (err) => [err, null], ); - assert(err === null); - assert.deepEqual(data.json(), { - userId: 1, - id: 1, - title: 'delectus aut autem', - completed: false, - }); + assert(data === null); + assert.equal( + err.message, + `Call error: HTTP request to "https://jsonplaceholder.typicode.com/todos/1" is not allowed (no allowedHosts match "jsonplaceholder.typicode.com")`, + ); } finally { await plugin.close(); } }); + + test('http works as expected when host is allowed', async () => { + const functions = { + env: { + async hello_world(context: CallContext, _off: bigint) { + await new Promise((resolve) => setTimeout(resolve, 100)); + return context.store('it works'); + }, + }, + }; + + const plugin = await createPlugin( + { wasm: [{ name: 'http', url: 'http://localhost:8124/wasm/http.wasm' }] }, + { useWasi: true, functions, runInWorker: true, allowedHosts: ['*.typicode.com'] }, + ); + + try { + const [err, data] = await plugin.call('http_get').then( + (data) => [null, data], + (err) => [err, null], + ); + + assert(err === null); + assert.deepEqual(data.json(), { + userId: 1, + id: 1, + title: 'delectus aut autem', + completed: false, + }); + } finally { + await plugin.close(); + } + }); } test('createPlugin fails as expected when calling unknown function', async () => { diff --git a/src/polyfills/deno-minimatch.ts b/src/polyfills/deno-minimatch.ts new file mode 100644 index 0000000..66c5141 --- /dev/null +++ b/src/polyfills/deno-minimatch.ts @@ -0,0 +1,5 @@ +import minimatch from 'https://deno.land/x/minimatch@v3.0.4/index.js'; + +export function matches(text: string, pattern: string): boolean { + return minimatch(text, pattern); +} diff --git a/src/polyfills/node-minimatch.ts b/src/polyfills/node-minimatch.ts new file mode 100644 index 0000000..183b60c --- /dev/null +++ b/src/polyfills/node-minimatch.ts @@ -0,0 +1,5 @@ +import { minimatch } from 'minimatch'; + +export function matches(text: string, pattern: string): boolean { + return minimatch(text, pattern); +} diff --git a/src/worker.ts b/src/worker.ts index 7bc66d0..2018f00 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -65,6 +65,10 @@ class Reactor { }, ); + this.dynamicHandlers.set('reset', async (_txf) => { + return this.plugin?.reset(); + }); + this.dynamicHandlers.set('getExports', async (_txf, name) => { return this.plugin?.getExports(name); }); @@ -267,20 +271,23 @@ class RingBufferReader { } close() { - const expected = this.flag[0]; while ( Atomics.compareExchange(this.flag, RingBufferReader.SAB_IDX, this.expected, RingBufferReader.SAB_BASE_OFFSET) !== RingBufferReader.SAB_BASE_OFFSET - ) {} + ) {} // eslint-disable-line no-empty Atomics.notify(this.flag, RingBufferReader.SAB_IDX, MAX_WAIT); } pull(reset: boolean = true) { if (reset) { while ( - Atomics.compareExchange(this.flag, RingBufferReader.SAB_IDX, this.expected, RingBufferReader.SAB_BASE_OFFSET) !== - RingBufferReader.SAB_BASE_OFFSET - ) {} + Atomics.compareExchange( + this.flag, + RingBufferReader.SAB_IDX, + this.expected, + RingBufferReader.SAB_BASE_OFFSET, + ) !== RingBufferReader.SAB_BASE_OFFSET + ) {} // eslint-disable-line no-empty Atomics.notify(this.flag, RingBufferReader.SAB_IDX, MAX_WAIT); } // host now copies out, once it's done it writes the available bytes to the flag. diff --git a/types/js-sdk/index.d.ts b/types/js-sdk/index.d.ts index 251c13a..8f13e5c 100644 --- a/types/js-sdk/index.d.ts +++ b/types/js-sdk/index.d.ts @@ -22,3 +22,6 @@ declare module 'js-sdk:wasi' { declare module 'js-sdk:worker-url' { declare const WORKER_URL: URL; } +declare module 'js-sdk:minimatch' { + function matches(text: string, pattern: string): boolean; +} From bf807149613e0f23531aabca3166ff189c9861f0 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Tue, 7 Nov 2023 14:06:27 -0800 Subject: [PATCH 21/25] fix: correct race condition on certain block offsets --- src/background-plugin.ts | 4 ++-- src/worker.ts | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/background-plugin.ts b/src/background-plugin.ts index 0620515..739f0b2 100644 --- a/src/background-plugin.ts +++ b/src/background-plugin.ts @@ -420,8 +420,8 @@ class RingBufferWriter { async flush() { if (this.outputOffset === RingBufferWriter.SAB_BASE_OFFSET) { - // always make sure we flush *something* - this.writeUint8(0); + // no need to flush -- we haven't written anything! + return; } const targetOffset = this.outputOffset; diff --git a/src/worker.ts b/src/worker.ts index 2018f00..9566bd6 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -177,7 +177,6 @@ class Reactor { if (!this.hostFlag) { throw new Error('attempted to call host before receiving shared array buffer'); } - const state = context[EXPORT_STATE](); this.port.postMessage({ From 40d0583c495cef9028f14066fe2c560449471156 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Tue, 7 Nov 2023 14:15:29 -0800 Subject: [PATCH 22/25] feat: alias createPlugin as newPlugin for folks coming from other extism sdks! --- src/mod.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mod.ts b/src/mod.ts index 3926ce2..3f3dc79 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -96,4 +96,6 @@ export async function createPlugin( return (opts.runInWorker ? _createBackgroundPlugin : _createForegroundPlugin)(ic, names, moduleData); } +export { createPlugin as newPlugin }; + export default createPlugin; From faf9ebb12a2d87c6270508b916a5f976b64361a5 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Wed, 8 Nov 2023 09:30:10 -0800 Subject: [PATCH 23/25] fix: s/DYLIBSO_ENV/EXTISM_ENV/g --- src/background-plugin.ts | 8 ++++---- src/foreground-plugin.ts | 4 ++-- src/worker.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/background-plugin.ts b/src/background-plugin.ts index 739f0b2..1ea4a93 100644 --- a/src/background-plugin.ts +++ b/src/background-plugin.ts @@ -3,7 +3,7 @@ import { PluginOutput, type InternalConfig } from './interfaces.ts'; import { WORKER_URL } from 'js-sdk:worker-url'; import { Worker } from 'node:worker_threads'; import { CAPABILITIES } from 'js-sdk:capabilities'; -import { DYLIBSO_ENV } from './foreground-plugin.ts'; +import { EXTISM_ENV } from './foreground-plugin.ts'; import { matches } from 'js-sdk:minimatch'; const MAX_WAIT = 5000; @@ -307,10 +307,10 @@ class HttpContext { } contribute(functions: Record>) { - functions[DYLIBSO_ENV] ??= {}; - functions[DYLIBSO_ENV].extism_http_request = (callContext: CallContext, reqaddr: bigint, bodyaddr: bigint) => + functions[EXTISM_ENV] ??= {}; + functions[EXTISM_ENV].extism_http_request = (callContext: CallContext, reqaddr: bigint, bodyaddr: bigint) => this.makeRequest(callContext, reqaddr, bodyaddr); - functions[DYLIBSO_ENV].extism_http_status_code = () => this.lastStatusCode; + functions[EXTISM_ENV].extism_http_status_code = () => this.lastStatusCode; } async makeRequest(callContext: CallContext, reqaddr: bigint, bodyaddr: bigint) { diff --git a/src/foreground-plugin.ts b/src/foreground-plugin.ts index 05e589d..829bd3a 100644 --- a/src/foreground-plugin.ts +++ b/src/foreground-plugin.ts @@ -2,7 +2,7 @@ import { CallContext, RESET, GET_BLOCK, BEGIN, END, ENV, STORE } from './call-co import { PluginOutput, type InternalConfig } from './interfaces.ts'; import { loadWasi } from 'js-sdk:wasi'; -export const DYLIBSO_ENV = 'env'; +export const EXTISM_ENV = 'env'; export class ForegroundPlugin { #context: CallContext; @@ -160,7 +160,7 @@ export async function createForegroundPlugin( const imports: Record> = { ...(wasi ? { wasi_snapshot_preview1: await wasi.importObject() } : {}), - [DYLIBSO_ENV]: context[ENV], + [EXTISM_ENV]: context[ENV], }; for (const namespace in opts.functions) { diff --git a/src/worker.ts b/src/worker.ts index 9566bd6..7c90b51 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -1,7 +1,7 @@ import { parentPort } from 'node:worker_threads'; import { - DYLIBSO_ENV, + EXTISM_ENV, ForegroundPlugin, createForegroundPlugin as _createForegroundPlugin, } from './foreground-plugin.ts'; From 73540134f5515147a917549f90e0cc50883a01ff Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Wed, 8 Nov 2023 10:41:46 -0800 Subject: [PATCH 24/25] fix: support node v18 --- README.md | 2 ++ justfile | 4 ++-- src/interfaces.ts | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1b92f4c..37813b8 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,8 @@ npm install @extism/extism@1.0.0-rc1 --save > **Note**: Keep in mind we will possibly have breaking changes b/w rc versions until we hit 1.0. +> **Note**: Node v18 users will need to invoke node with `--experimental-global-webcrypto` to use hash checking. + ## Getting Started This guide should walk you through some of the concepts in Extism and this JS library. diff --git a/justfile b/justfile index a51401e..4eb9c8b 100644 --- a/justfile +++ b/justfile @@ -270,8 +270,8 @@ test: build && test-artifacts sleep 0.5 deno test -A src/mod.test.ts - node --no-warnings --test dist/tests/cjs/*.test.js - node --no-warnings --test dist/tests/esm/*.test.js + node --no-warnings --test --experimental-global-webcrypto dist/tests/cjs/*.test.js + node --no-warnings --test --experimental-global-webcrypto dist/tests/esm/*.test.js if &>/dev/null which bun; then bun run dist/tests/bun/*.test.js; fi playwright test --browser all tests/playwright.test.js diff --git a/src/interfaces.ts b/src/interfaces.ts index b214837..4107035 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -209,7 +209,8 @@ export interface ManifestWasmPath { * plugin embeds multiple Wasm modules. * * @property hash The expected SHA-256 hash of the associated Wasm module data. {@link createPlugin} validates incoming Wasm against - * provided hashes. + * provided hashes. If running on Node v18, `node` must be invoked using the `--experimental-global-webcrypto` flag. + * */ export type ManifestWasm = (ManifestWasmUrl | ManifestWasmData | ManifestWasmPath) & { name?: string | undefined; From a31aa1b95a0d136b80bfed94c09a23a30a6bf0f2 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Thu, 9 Nov 2023 10:40:51 -0800 Subject: [PATCH 25/25] fix: update readme --- README.md | 97 +++++++++++++++++++++++++++++---------------------- src/worker.ts | 3 +- 2 files changed, 56 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 37813b8..7aef8bb 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,9 @@ > **Note**: This houses the 1.0 version of the JavaScript SDK and is a work in progress. Please use the [Node SDK](https://github.com/extism/extism/tree/main/node) or the [Browser SDK](https://github.com/extism/extism/tree/main/browser) in extism/extism until we hit 1.0. -This is a universal JavaScript SDK for Extism. We are aiming for it to work in all the major -JavaScript runtimes: +This is a universal JavaScript SDK for Extism. It works in all the major JavaScript runtimes: -* Browsers +* Browsers (Firefox, Chrome, WebKit) * Node * Deno * Bun @@ -21,7 +20,17 @@ npm install @extism/extism@1.0.0-rc1 --save > **Note**: Keep in mind we will possibly have breaking changes b/w rc versions until we hit 1.0. -> **Note**: Node v18 users will need to invoke node with `--experimental-global-webcrypto` to use hash checking. +## Compatibility + +- **Node.js**: `v18+` (with `--experimental-global-webcrypto`); `v20` with no additional flags +- **Deno**: `v1.36+` +- **Bun**: Tested on `v1.0.7`; Bun partially implements WASI. + +Browser tests are run using [playwright](https://playwright.dev)'s defaults. In +browsers, background thread support requires `SharedArrayBuffer` and `Atomic` +support. This is only available in +[`crossOriginIsolated`](https://developer.mozilla.org/en-US/docs/Web/API/crossOriginIsolated) +contexts. ## Getting Started @@ -36,7 +45,7 @@ const createPlugin = require("@extism/extism") import createPlugin from '@extism/extism'; // Deno -import createPlugin from 'https://raw.githubusercontent.com/extism/js-sdk/main/src/deno/mod.ts'; +import createPlugin from 'https://raw.githubusercontent.com/extism/js-sdk/main/src/mod.ts'; ``` ## Creating A Plug-in @@ -54,40 +63,41 @@ const plugin = await createPlugin( ## Calling A Plug-in's Exports -This plug-in was written in Rust and it does one thing, it counts vowels in a -string. As such, it exposes one "export" function: `count_vowels`. We can call -exports using `ExtismPlugin.call`: +We're using a plug-in, `count_vowels`, which was compiled from Rust. +`count_vowels` plug-in does one thing: it counts vowels in a string. As such, +it exposes one "export" function: `count_vowels`. We can call exports using +`Plugin.call`: ```js -let out = await plugin.call("count_vowels", new TextEncoder().encode(input)); -console.log(new TextDecoder().decode(out.buffer)) +let out = await plugin.call("count_vowels", input); +console.log(out.text()) // => {"count": 3, "total": 3, "vowels": "aeiouAEIOU"} ``` -All exports have a simple interface of optional bytes in, and optional bytes -out. This plug-in happens to take a string and return a JSON encoded string -with a report of results. +All plug-in exports have a simple interface of optional bytes in, and optional +bytes out. This plug-in happens to take a string and return a JSON encoded +string with a report of results. ### Plug-in State -Plug-ins may be stateful or stateless. Plug-ins can maintain state b/w calls by -the use of variables. Our count vowels plug-in remembers the total number of -vowels it's ever counted in the "total" key in the result. You can see this by +Plug-ins may be stateful or stateless. Plug-ins can maintain state between calls by +the use of variables. Our `count_vowels` plug-in remembers the total number of +vowels it's ever counted in the `total` key in the result. You can see this by making subsequent calls to the export: ```js -let out = await plugin.call("count_vowels", new TextEncoder().encode("Hello, World!")); -console.log(new TextDecoder().decode(out.buffer)) - +let out = await plugin.call("count_vowels", "Hello, World!"); +console.log(out.text()) // => {"count": 3, "total": 9, "vowels": "aeiouAEIOU"} -out = await plugin.call("count_vowels", new TextEncoder().encode("Hello, World!")); -console.log(new TextDecoder().decode(out.buffer)) +out = await plugin.call("count_vowels", "Hello, World!"); +console.log(out.json()) // => {"count": 3, "total": 9, "vowels": "aeiouAEIOU"} ``` -These variables will persist until this plug-in is freed or you initialize a new one. +These variables will persist until you call `await plugin.reset()`. Variables +are not shared between plugin instances. ### Configuration @@ -104,8 +114,8 @@ let plugin = await createPlugin(wasm, { useWasi: true, }); -let out = await plugin.call("count_vowels", new TextEncoder().encode("Yellow, World!")); -console.log(new TextDecoder().decode(out.buffer)) +let out = await plugin.call("count_vowels", "Yellow, World!"); +console.log(out.text()) // => {"count": 3, "total": 3, "vowels": "aeiouAEIOU"} plugin = await createPlugin(wasm, { @@ -113,8 +123,8 @@ plugin = await createPlugin(wasm, { config: { "vowels": "aeiouyAEIOUY" } }); -out = await plugin.call("count_vowels", new TextEncoder().encode("Yellow, World!")); -console.log(new TextDecoder().decode(out.buffer)) +out = await plugin.call("count_vowels", "Yellow, World!"); +console.log(out.text()) // => {"count": 4, "total": 4, "vowels": "aeiouAEIOUY"} ``` @@ -141,7 +151,7 @@ const wasm = { } ``` -> *Note*: The source code for this is [here](https://github.com/extism/plugins/blob/main/count_vowels_kvstore/src/lib.rs) and is written in rust, but it could be written in any of our PDK languages. +> *Note*: The source code for this is [here](https://github.com/extism/plugins/blob/main/count_vowels_kvstore/src/lib.rs) and is written in Rust, but it could be written in any of our PDK languages. Unlike our previous plug-in, this plug-in expects you to provide host functions that satisfy our its import interface for a KV store. @@ -153,20 +163,24 @@ let kvStore = new Map(); const options = { useWasi: true, functions: { - "env": { + env: { // NOTE: the first argument is always a CurrentPlugin - "kv_read": function (cp: CurrentPlugin, offs: bigint) { - const key = cp.readString(offs); + kv_read(cp: CurrentPlugin, offs: bigint) { + const key = cp.read(offs).text(); let value = kvStore.get(key) ?? new Uint8Array([0, 0, 0, 0]); console.log(`Read ${new DataView(value.buffer).getUint32(0, true)} from key=${key}`); - return cp.writeBytes(value); + return cp.store(value); }, - "kv_write": function (cp: CurrentPlugin, kOffs: bigint, vOffs: bigint) { // this: CurrentPlugin - const key = cp.readString(kOffs); - const value = cp.readBytes(vOffs); - console.log(`Writing value=${new DataView(value.buffer).getUint32(0, true)} from key=${key}`); + kv_write(cp: CurrentPlugin, kOffs: bigint, vOffs: bigint) { + const key = cp.read(kOffs).text(); + + // Value is a PluginOutput, which subclasses DataView. Along + // with the `text()` and `json()` methods we've seen, we also + // get DataView methods, such as `getUint32`. + const value = cp.read(vOffs); + console.log(`Writing value=${new value.getUint32(0, true)} from key=${key}`); - kvStore.set(key, value); + kvStore.set(key, value.bytes()); } } } @@ -174,8 +188,7 @@ const options = { ``` > *Note*: In order to write host functions you should get familiar with the -> methods on the `CurrentPlugin` type. `this` is bound to an instance of -> `CurrentPlugin`. +> methods on the `CurrentPlugin` type. We need to pass these imports to the plug-in to create them. All imports of a plug-in must be satisfied for it to be initialized: @@ -187,14 +200,14 @@ const plugin = await createPlugin(wasm, options); Now we can invoke the event: ```js -let out = await plugin.call("count_vowels", new TextEncoder().encode("Hello World!")); -console.log(new TextDecoder().decode(out.buffer)) +let out = await plugin.call("count_vowels", "Hello World!"); +console.log(out.text()) // => Read from key=count-vowels" // => Writing value=3 from key=count-vowels" // => {"count": 3, "total": 3, "vowels": "aeiouAEIOU"} -out = await plugin.call("count_vowels", new TextEncoder().encode("Hello World!")); -console.log(new TextDecoder().decode(out.buffer)) +out = await plugin.call("count_vowels", "Hello World!"); +console.log(out.text()) // => Read from key=count-vowels" // => Writing value=6 from key=count-vowels" // => {"count": 3, "total": 6, "vowels": "aeiouAEIOU"} diff --git a/src/worker.ts b/src/worker.ts index 7c90b51..ea45c0f 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -1,7 +1,6 @@ import { parentPort } from 'node:worker_threads'; import { - EXTISM_ENV, ForegroundPlugin, createForegroundPlugin as _createForegroundPlugin, } from './foreground-plugin.ts'; @@ -242,7 +241,7 @@ class Reactor { } } -const _CONTEXT = new Reactor(parentPort); +new Reactor(parentPort); class RingBufferReader { input: SharedArrayBuffer;