diff --git a/.github/workflows/startos-iso.yaml b/.github/workflows/startos-iso.yaml index 3497624e7..3490936b4 100644 --- a/.github/workflows/startos-iso.yaml +++ b/.github/workflows/startos-iso.yaml @@ -187,11 +187,14 @@ jobs: run: | mkdir -p web/node_modules mkdir -p web/dist/raw + mkdir -p core/startos/bindings + mkdir -p sdk/base/lib/osBindings mkdir -p container-runtime/node_modules mkdir -p container-runtime/dist mkdir -p container-runtime/dist/node_modules mkdir -p core/startos/bindings mkdir -p sdk/dist + mkdir -p sdk/baseDist mkdir -p patch-db/client/node_modules mkdir -p patch-db/client/dist mkdir -p web/.angular diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 0a5eb38e9..3f47a65a4 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -11,7 +11,7 @@ on: - next/* env: - NODEJS_VERSION: "18.15.0" + NODEJS_VERSION: "20.16.0" ENVIRONMENT: dev-unstable jobs: diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index cb9385f41..c3c555a05 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -27,6 +27,7 @@ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash source ~/.bashrc nvm install 20 nvm use 20 +nvm alias default 20 # this prevents your machine from reverting back to another version ``` ## Cloning the repository diff --git a/Makefile b/Makefile index d085117b2..d7ff9ec6c 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ COMPAT_SRC := $(shell git ls-files system-images/compat/) UTILS_SRC := $(shell git ls-files system-images/utils/) BINFMT_SRC := $(shell git ls-files system-images/binfmt/) CORE_SRC := $(shell git ls-files core) $(shell git ls-files --recurse-submodules patch-db) $(GIT_HASH_FILE) -WEB_SHARED_SRC := $(shell git ls-files web/projects/shared) $(shell ls -p web/ | grep -v / | sed 's/^/web\//g') web/node_modules/.package-lock.json web/config.json patch-db/client/dist/index.js web/patchdb-ui-seed.json sdk/dist/package.json +WEB_SHARED_SRC := $(shell git ls-files web/projects/shared) $(shell ls -p web/ | grep -v / | sed 's/^/web\//g') web/node_modules/.package-lock.json web/config.json patch-db/client/dist/index.js sdk/baseDist/package.json web/patchdb-ui-seed.json sdk/dist/package.json WEB_UI_SRC := $(shell git ls-files web/projects/ui) WEB_SETUP_WIZARD_SRC := $(shell git ls-files web/projects/setup-wizard) WEB_INSTALL_WIZARD_SRC := $(shell git ls-files web/projects/install-wizard) @@ -48,7 +48,7 @@ endif .DELETE_ON_ERROR: -.PHONY: all metadata install clean format cli uis ui reflash deb $(IMAGE_TYPE) squashfs sudo wormhole wormhole-deb test test-core test-sdk test-container-runtime +.PHONY: all metadata install clean format cli uis ui reflash deb $(IMAGE_TYPE) squashfs sudo wormhole wormhole-deb test test-core test-sdk test-container-runtime registry all: $(ALL_TARGETS) @@ -95,7 +95,7 @@ test: | test-core test-sdk test-container-runtime test-core: $(CORE_SRC) $(ENVIRONMENT_FILE) ./core/run-tests.sh -test-sdk: $(shell git ls-files sdk) sdk/lib/osBindings/index.ts +test-sdk: $(shell git ls-files sdk) sdk/base/lib/osBindings/index.ts cd sdk && make test test-container-runtime: container-runtime/node_modules/.package-lock.json $(shell git ls-files container-runtime/src) container-runtime/package.json container-runtime/tsconfig.json @@ -104,6 +104,9 @@ test-container-runtime: container-runtime/node_modules/.package-lock.json $(shel cli: cd core && ./install-cli.sh +registry: + cd core && ./build-registrybox.sh + deb: results/$(BASENAME).deb debian/control: build/lib/depends build/lib/conflicts @@ -223,20 +226,22 @@ container-runtime/node_modules/.package-lock.json: container-runtime/package.jso npm --prefix container-runtime ci touch container-runtime/node_modules/.package-lock.json -sdk/lib/osBindings/index.ts: core/startos/bindings/index.ts - rsync -ac --delete core/startos/bindings/ sdk/lib/osBindings/ - touch sdk/lib/osBindings/index.ts +sdk/base/lib/osBindings/index.ts: core/startos/bindings/index.ts + mkdir -p sdk/base/lib/osBindings + rsync -ac --delete core/startos/bindings/ sdk/base/lib/osBindings/ + touch sdk/base/lib/osBindings/index.ts core/startos/bindings/index.ts: $(shell git ls-files core) $(ENVIRONMENT_FILE) rm -rf core/startos/bindings ./core/build-ts.sh - ls core/startos/bindings/*.ts | sed 's/core\/startos\/bindings\/\([^.]*\)\.ts/export { \1 } from ".\/\1";/g' > core/startos/bindings/index.ts - npm --prefix sdk exec -- prettier --config ./sdk/package.json -w ./core/startos/bindings/*.ts + ls core/startos/bindings/*.ts | sed 's/core\/startos\/bindings\/\([^.]*\)\.ts/export { \1 } from ".\/\1";/g' | grep -v '"./index"' | tee core/startos/bindings/index.ts + npm --prefix sdk exec -- prettier --config ./sdk/base/package.json -w ./core/startos/bindings/*.ts touch core/startos/bindings/index.ts -sdk/dist/package.json: $(shell git ls-files sdk) sdk/lib/osBindings/index.ts +sdk/dist/package.json sdk/baseDist/package.json: $(shell git ls-files sdk) sdk/base/lib/osBindings/index.ts (cd sdk && make bundle) touch sdk/dist/package.json + touch sdk/baseDist/package.json # TODO: make container-runtime its own makefile? container-runtime/dist/index.js: container-runtime/node_modules/.package-lock.json $(shell git ls-files container-runtime/src) container-runtime/package.json container-runtime/tsconfig.json @@ -272,11 +277,11 @@ core/target/$(ARCH)-unknown-linux-musl/release/containerbox: $(CORE_SRC) $(ENVIR ARCH=$(ARCH) ./core/build-containerbox.sh touch core/target/$(ARCH)-unknown-linux-musl/release/containerbox -web/node_modules/.package-lock.json: web/package.json sdk/dist/package.json +web/node_modules/.package-lock.json: web/package.json sdk/baseDist/package.json npm --prefix web ci touch web/node_modules/.package-lock.json -web/.angular/.updated: patch-db/client/dist/index.js sdk/dist/package.json web/node_modules/.package-lock.json +web/.angular/.updated: patch-db/client/dist/index.js sdk/baseDist/package.json web/node_modules/.package-lock.json rm -rf web/.angular mkdir -p web/.angular touch web/.angular/.updated @@ -329,4 +334,4 @@ cargo-deps/$(ARCH)-unknown-linux-musl/release/tokio-console: ARCH=$(ARCH) PREINSTALL="apk add musl-dev pkgconfig" ./build-cargo-dep.sh tokio-console cargo-deps/$(ARCH)-unknown-linux-musl/release/startos-backup-fs: - ARCH=$(ARCH) PREINSTALL="apk add fuse3 fuse3-dev fuse3-static musl-dev pkgconfig" ./build-cargo-dep.sh --git https://github.com/Start9Labs/start-fs.git startos-backup-fs \ No newline at end of file + ARCH=$(ARCH) PREINSTALL="apk add fuse3 fuse3-dev fuse3-static musl-dev pkgconfig" ./build-cargo-dep.sh --git https://github.com/Start9Labs/start-fs.git startos-backup-fs diff --git a/container-runtime/package-lock.json b/container-runtime/package-lock.json index 2fddf23f2..2ff4bb8c6 100644 --- a/container-runtime/package-lock.json +++ b/container-runtime/package-lock.json @@ -20,7 +20,6 @@ "node-fetch": "^3.1.0", "ts-matches": "^5.5.1", "tslib": "^2.5.3", - "tslog": "^4.9.3", "typescript": "^5.1.3", "yaml": "^2.3.1" }, @@ -36,9 +35,36 @@ "typescript": ">5.2" } }, + "../sdk/baseDist": { + "name": "@start9labs/start-sdk-base", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@iarna/toml": "^2.2.5", + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0", + "isomorphic-fetch": "^3.0.0", + "lodash.merge": "^4.6.2", + "mime": "^4.0.3", + "ts-matches": "^5.5.1", + "yaml": "^2.2.2" + }, + "devDependencies": { + "@types/jest": "^29.4.0", + "@types/lodash.merge": "^4.6.2", + "jest": "^29.4.3", + "peggy": "^3.0.2", + "prettier": "^3.2.5", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.1", + "ts-pegjs": "^4.2.1", + "tsx": "^4.7.1", + "typescript": "^5.0.4" + } + }, "../sdk/dist": { "name": "@start9labs/start-sdk", - "version": "0.3.6-alpha6", + "version": "0.3.6-alpha8", "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", @@ -5628,17 +5654,6 @@ "version": "2.6.3", "license": "0BSD" }, - "node_modules/tslog": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/tslog/-/tslog-4.9.3.tgz", - "integrity": "sha512-oDWuGVONxhVEBtschLf2cs/Jy8i7h1T+CpdkTNWQgdAF7DhRo2G8vMCgILKe7ojdEkLhICWgI1LYSSKaJsRgcw==", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/fullstack-build/tslog?sponsor=1" - } - }, "node_modules/type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -9758,11 +9773,6 @@ "tslib": { "version": "2.6.3" }, - "tslog": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/tslog/-/tslog-4.9.3.tgz", - "integrity": "sha512-oDWuGVONxhVEBtschLf2cs/Jy8i7h1T+CpdkTNWQgdAF7DhRo2G8vMCgILKe7ojdEkLhICWgI1LYSSKaJsRgcw==" - }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", diff --git a/container-runtime/readme.md b/container-runtime/readme.md deleted file mode 100644 index 023091463..000000000 --- a/container-runtime/readme.md +++ /dev/null @@ -1,86 +0,0 @@ -## Testing - -So, we are going to - -1. create a fake server -2. pretend socket server os (while the fake server is running) -3. Run a fake effects system (while 1/2 are running) - -In order to simulate that we created a server like the start-os and -a fake server (in this case I am using syncthing-wrapper) - -### TODO - -Undo the packing that I have done earlier, and hijack the embassy.js to use the bundle service + code - -Converting embassy.js -> service.js - -```sequence {theme="hand"} -startOs ->> startInit.js: Rpc Call -startInit.js ->> service.js: Rpc Converted into js code -``` - -### Create a fake server - -```bash -run_test () { - ( - set -e - libs=/home/jh/Projects/start-os/libs/start_init - sockets=/tmp/start9 - service=/home/jh/Projects/syncthing-wrapper - - docker run \ - -v $libs:/libs \ - -v $service:/service \ - -w /libs \ - --rm node:18-alpine \ - sh -c " - npm i && - npm run bundle:esbuild && - npm run bundle:service - " - - - - docker run \ - -v ./libs/start_init/:/libs \ - -w /libs \ - --rm node:18-alpine \ - sh -c " - npm i && - npm run bundle:esbuild - " - - - - rm -rf $sockets || true - mkdir -p $sockets/sockets - cd $service - docker run \ - -v $libs:/start-init \ - -v $sockets:/start9 \ - --rm -it $(docker build -q .) sh -c " - apk add nodejs && - node /start-init/bundleEs.js - " - ) -} -run_test -``` - -### Pretend Socket Server OS - -First we are going to create our fake server client with the bash then send it the json possible data - -```bash -sudo socat - unix-client:/tmp/start9/sockets/rpc.sock -``` - - -```json -{"id":"a","method":"run","params":{"methodName":"/dependencyMounts","methodArgs":[]}} -{"id":"a","method":"run","params":{"methodName":"/actions/test","methodArgs":{"input":{"id": 1}}}} -{"id":"b","method":"run","params":{"methodName":"/actions/test","methodArgs":{"id": 1}}} - -``` diff --git a/container-runtime/src/Adapters/EffectCreator.ts b/container-runtime/src/Adapters/EffectCreator.ts index 3338560c9..022d697ab 100644 --- a/container-runtime/src/Adapters/EffectCreator.ts +++ b/container-runtime/src/Adapters/EffectCreator.ts @@ -4,7 +4,7 @@ import { object, string, number, literals, some, unknown } from "ts-matches" import { Effects } from "../Models/Effects" import { CallbackHolder } from "../Models/CallbackHolder" -import { MainEffects } from "@start9labs/start-sdk/cjs/lib/StartSdk" +import { asError } from "@start9labs/start-sdk/base/lib/util" const matchRpcError = object({ error: object( { @@ -35,7 +35,8 @@ let hostSystemId = 0 export type EffectContext = { procedureId: string | null - callbacks: CallbackHolder | null + callbacks?: CallbackHolder + constRetry: () => void } const rpcRoundFor = @@ -50,7 +51,7 @@ const rpcRoundFor = JSON.stringify({ id, method, - params: { ...params, procedureId }, + params: { ...params, procedureId: procedureId || undefined }, }) + "\n", ) }) @@ -67,7 +68,7 @@ const rpcRoundFor = let message = res.error.message console.error( "Error in host RPC:", - utils.asError({ method, params }), + utils.asError({ method, params, error: res.error }), ) if (string.test(res.error.data)) { message += ": " + res.error.data @@ -100,9 +101,49 @@ const rpcRoundFor = }) } -function makeEffects(context: EffectContext): Effects { +export function makeEffects(context: EffectContext): Effects { const rpcRound = rpcRoundFor(context.procedureId) const self: Effects = { + constRetry: context.constRetry, + clearCallbacks(...[options]: Parameters) { + return rpcRound("clear-callbacks", { + ...options, + }) as ReturnType + }, + action: { + clear(...[options]: Parameters) { + return rpcRound("action.clear", { + ...options, + }) as ReturnType + }, + export(...[options]: Parameters) { + return rpcRound("action.export", { + ...options, + }) as ReturnType + }, + getInput(...[options]: Parameters) { + return rpcRound("action.get-input", { + ...options, + }) as ReturnType + }, + request(...[options]: Parameters) { + return rpcRound("action.request", { + ...options, + }) as ReturnType + }, + run(...[options]: Parameters) { + return rpcRound("action.run", { + ...options, + }) as ReturnType + }, + clearRequests( + ...[options]: Parameters + ) { + return rpcRound("action.clear-requests", { + ...options, + }) as ReturnType + }, + }, bind(...[options]: Parameters) { return rpcRound("bind", { ...options, @@ -138,16 +179,6 @@ function makeEffects(context: EffectContext): Effects { > }, }, - executeAction(...[options]: Parameters) { - return rpcRound("execute-action", options) as ReturnType< - T.Effects["executeAction"] - > - }, - exportAction(...[options]: Parameters) { - return rpcRound("export-action", options) as ReturnType< - T.Effects["exportAction"] - > - }, exportServiceInterface: (( ...[options]: Parameters ) => { @@ -162,11 +193,6 @@ function makeEffects(context: EffectContext): Effects { T.Effects["exposeForDependents"] > }, - getConfigured(...[]: Parameters) { - return rpcRound("get-configured", {}) as ReturnType< - T.Effects["getConfigured"] - > - }, getContainerIp(...[]: Parameters) { return rpcRound("get-container-ip", {}) as ReturnType< T.Effects["getContainerIp"] @@ -230,19 +256,9 @@ function makeEffects(context: EffectContext): Effects { mount(...[options]: Parameters) { return rpcRound("mount", options) as ReturnType }, - clearActions(...[]: Parameters) { - return rpcRound("clear-actions", {}) as ReturnType< - T.Effects["clearActions"] - > - }, restart(...[]: Parameters) { return rpcRound("restart", {}) as ReturnType }, - setConfigured(...[configured]: Parameters) { - return rpcRound("set-configured", { configured }) as ReturnType< - T.Effects["setConfigured"] - > - }, setDependencies( dependencies: Parameters[0], ): ReturnType { @@ -299,18 +315,3 @@ function makeEffects(context: EffectContext): Effects { } return self } - -export function makeProcedureEffects(procedureId: string): Effects { - return makeEffects({ procedureId, callbacks: null }) -} - -export function makeMainEffects(): MainEffects { - const rpcRound = rpcRoundFor(null) - return { - _type: "main", - clearCallbacks: () => { - return rpcRound("clearCallbacks", {}) as Promise - }, - ...makeEffects({ procedureId: null, callbacks: new CallbackHolder() }), - } -} diff --git a/container-runtime/src/Adapters/RpcListener.ts b/container-runtime/src/Adapters/RpcListener.ts index 458b1af0f..de2bb7f1a 100644 --- a/container-runtime/src/Adapters/RpcListener.ts +++ b/container-runtime/src/Adapters/RpcListener.ts @@ -14,17 +14,14 @@ import { anyOf, } from "ts-matches" -import { types as T } from "@start9labs/start-sdk" +import { types as T, utils } from "@start9labs/start-sdk" import * as fs from "fs" import { CallbackHolder } from "../Models/CallbackHolder" import { AllGetDependencies } from "../Interfaces/AllGetDependencies" import { jsonPath, unNestPath } from "../Models/JsonPath" -import { RunningMain, System } from "../Interfaces/System" -import { - MakeMainEffects, - MakeProcedureEffects, -} from "../Interfaces/MakeEffects" +import { System } from "../Interfaces/System" +import { makeEffects } from "./EffectCreator" type MaybePromise = T | Promise export const matchRpcResult = anyOf( object({ result: any }), @@ -55,33 +52,39 @@ const jsonrpc = "2.0" as const const isResult = object({ result: any }).test const idType = some(string, number, literal(null)) -type IdType = null | string | number -const runType = object({ - id: idType, - method: literal("execute"), - params: object( - { - id: string, - procedure: string, - input: any, - timeout: number, - }, - ["timeout"], - ), -}) -const sandboxRunType = object({ - id: idType, - method: literal("sandbox"), - params: object( - { - id: string, - procedure: string, - input: any, - timeout: number, - }, - ["timeout"], - ), -}) +type IdType = null | string | number | undefined +const runType = object( + { + id: idType, + method: literal("execute"), + params: object( + { + id: string, + procedure: string, + input: any, + timeout: number, + }, + ["timeout"], + ), + }, + ["id"], +) +const sandboxRunType = object( + { + id: idType, + method: literal("sandbox"), + params: object( + { + id: string, + procedure: string, + input: any, + timeout: number, + }, + ["timeout"], + ), + }, + ["id"], +) const callbackType = object({ method: literal("callback"), params: object({ @@ -89,29 +92,44 @@ const callbackType = object({ args: array, }), }) -const initType = object({ - id: idType, - method: literal("init"), -}) -const startType = object({ - id: idType, - method: literal("start"), -}) -const stopType = object({ - id: idType, - method: literal("stop"), -}) -const exitType = object({ - id: idType, - method: literal("exit"), -}) -const evalType = object({ - id: idType, - method: literal("eval"), - params: object({ - script: string, - }), -}) +const initType = object( + { + id: idType, + method: literal("init"), + }, + ["id"], +) +const startType = object( + { + id: idType, + method: literal("start"), + }, + ["id"], +) +const stopType = object( + { + id: idType, + method: literal("stop"), + }, + ["id"], +) +const exitType = object( + { + id: idType, + method: literal("exit"), + }, + ["id"], +) +const evalType = object( + { + id: idType, + method: literal("eval"), + params: object({ + script: string, + }), + }, + ["id"], +) const jsonParse = (x: string) => JSON.parse(x) @@ -144,8 +162,7 @@ const hasId = object({ id: idType }).test export class RpcListener { unixSocketServer = net.createServer(async (server) => {}) private _system: System | undefined - private _makeProcedureEffects: MakeProcedureEffects | undefined - private _makeMainEffects: MakeMainEffects | undefined + private callbacks: CallbackHolder | undefined constructor(readonly getDependencies: AllGetDependencies) { if (!fs.existsSync(SOCKET_PARENT)) { @@ -212,18 +229,33 @@ export class RpcListener { return this._system } - private get makeProcedureEffects() { - if (!this._makeProcedureEffects) { - this._makeProcedureEffects = this.getDependencies.makeProcedureEffects() + private callbackHolders: Map = new Map() + private removeCallbackHolderFor(procedure: string) { + const prev = this.callbackHolders.get(procedure) + if (prev) { + this.callbackHolders.delete(procedure) + this.callbacks?.removeChild(prev) } - return this._makeProcedureEffects + } + private callbackHolderFor(procedure: string): CallbackHolder { + this.removeCallbackHolderFor(procedure) + const callbackHolder = this.callbacks!.child() + this.callbackHolders.set(procedure, callbackHolder) + return callbackHolder } - private get makeMainEffects() { - if (!this._makeMainEffects) { - this._makeMainEffects = this.getDependencies.makeMainEffects() + callCallback(callback: number, args: any[]): void { + if (this.callbacks) { + this.callbacks + .callCallback(callback, args) + .catch((error) => + console.error(`callback ${callback} failed`, utils.asError(error)), + ) + } else { + console.warn( + `callback ${callback} ignored because system is not initialized`, + ) } - return this._makeMainEffects } private dealWithInput(input: unknown): MaybePromise { @@ -231,40 +263,49 @@ export class RpcListener { .when(runType, async ({ id, params }) => { const system = this.system const procedure = jsonPath.unsafeCast(params.procedure) - const effects = this.getDependencies.makeProcedureEffects()(params.id) - const input = params.input - const timeout = params.timeout - const result = getResult(procedure, system, effects, timeout, input) + const { input, timeout, id: procedureId } = params + const result = this.getResult( + procedure, + system, + procedureId, + timeout, + input, + ) return handleRpc(id, result) }) .when(sandboxRunType, async ({ id, params }) => { const system = this.system const procedure = jsonPath.unsafeCast(params.procedure) - const effects = this.makeProcedureEffects(params.id) - const result = getResult( + const { input, timeout, id: procedureId } = params + const result = this.getResult( procedure, system, - effects, - params.input, - params.input, + procedureId, + timeout, + input, ) return handleRpc(id, result) }) .when(callbackType, async ({ params: { callback, args } }) => { - this.system.callCallback(callback, args) + this.callCallback(callback, args) return null }) .when(startType, async ({ id }) => { + const callbacks = this.callbackHolderFor("main") + const effects = makeEffects({ + procedureId: null, + callbacks, + constRetry: () => {}, + }) return handleRpc( id, - this.system - .start(this.makeMainEffects()) - .then((result) => ({ result })), + this.system.start(effects).then((result) => ({ result })), ) }) .when(stopType, async ({ id }) => { + this.removeCallbackHolderFor("main") return handleRpc( id, this.system.stop().then((result) => ({ result })), @@ -284,7 +325,20 @@ export class RpcListener { (async () => { if (!this._system) { const system = await this.getDependencies.system() - await system.containerInit() + this.callbacks = new CallbackHolder( + makeEffects({ + procedureId: null, + constRetry: () => {}, + }), + ) + const callbacks = this.callbackHolderFor("containerInit") + await system.containerInit( + makeEffects({ + procedureId: null, + callbacks, + constRetry: () => {}, + }), + ) this._system = system } })().then((result) => ({ result })), @@ -316,17 +370,20 @@ export class RpcListener { })(), ) }) - .when(shape({ id: idType, method: string }), ({ id, method }) => ({ - jsonrpc, - id, - error: { - code: -32601, - message: `Method not found`, - data: { - details: method, + .when( + shape({ id: idType, method: string }, ["id"]), + ({ id, method }) => ({ + jsonrpc, + id, + error: { + code: -32601, + message: `Method not found`, + data: { + details: method, + }, }, - }, - })) + }), + ) .defaultToLazy(() => { console.warn( @@ -345,98 +402,84 @@ export class RpcListener { } }) } -} -function getResult( - procedure: typeof jsonPath._TYPE, - system: System, - effects: T.Effects, - timeout: number | undefined, - input: any, -) { - const ensureResultTypeShape = ( - result: - | void - | T.ConfigRes - | T.PropertiesReturn - | T.ActionMetadata[] - | T.ActionResult, - ): { result: any } => { - if (isResult(result)) return result - return { result } - } - return (async () => { - switch (procedure) { - case "/backup/create": - return system.createBackup(effects, timeout || null) - case "/backup/restore": - return system.restoreBackup(effects, timeout || null) - case "/config/get": - return system.getConfig(effects, timeout || null) - case "/config/set": - return system.setConfig(effects, input, timeout || null) - case "/properties": - return system.properties(effects, timeout || null) - case "/actions/metadata": - return system.actionsMetadata(effects) - case "/init": - return system.packageInit( - effects, - string.optional().unsafeCast(input), - timeout || null, - ) - case "/uninit": - return system.packageUninit( - effects, - string.optional().unsafeCast(input), - timeout || null, - ) - default: - const procedures = unNestPath(procedure) - switch (true) { - case procedures[1] === "actions" && procedures[3] === "get": - return system.action(effects, procedures[2], input, timeout || null) - case procedures[1] === "actions" && procedures[3] === "run": - return system.action(effects, procedures[2], input, timeout || null) - case procedures[1] === "dependencies" && procedures[3] === "query": - return system.dependenciesAutoconfig( - effects, - procedures[2], - input, - timeout || null, - ) - - case procedures[1] === "dependencies" && procedures[3] === "update": - return system.dependenciesAutoconfig( - effects, - procedures[2], - input, - timeout || null, - ) - } + private getResult( + procedure: typeof jsonPath._TYPE, + system: System, + procedureId: string, + timeout: number | undefined, + input: any, + ) { + const ensureResultTypeShape = ( + result: void | T.ActionInput | T.PropertiesReturn | T.ActionResult | null, + ): { result: any } => { + if (isResult(result)) return result + return { result } } - })().then(ensureResultTypeShape, (error) => - matches(error) - .when( - object( - { - error: string, - code: number, - }, - ["code"], - { code: 0 }, - ), - (error) => ({ + const callbacks = this.callbackHolderFor(procedure) + const effects = makeEffects({ + procedureId, + callbacks, + constRetry: () => {}, + }) + + return (async () => { + switch (procedure) { + case "/backup/create": + return system.createBackup(effects, timeout || null) + case "/backup/restore": + return system.restoreBackup(effects, timeout || null) + case "/properties": + return system.properties(effects, timeout || null) + case "/packageInit": + return system.packageInit(effects, timeout || null) + case "/packageUninit": + return system.packageUninit( + effects, + string.optional().unsafeCast(input), + timeout || null, + ) + default: + const procedures = unNestPath(procedure) + switch (true) { + case procedures[1] === "actions" && procedures[3] === "getInput": + return system.getActionInput( + effects, + procedures[2], + timeout || null, + ) + case procedures[1] === "actions" && procedures[3] === "run": + return system.runAction( + effects, + procedures[2], + input.input, + timeout || null, + ) + } + } + })().then(ensureResultTypeShape, (error) => + matches(error) + .when( + object( + { + error: string, + code: number, + }, + ["code"], + { code: 0 }, + ), + (error) => ({ + error: { + code: error.code, + message: error.error, + }, + }), + ) + .defaultToLazy(() => ({ error: { - code: error.code, - message: error.error, + code: 0, + message: String(error), }, - }), - ) - .defaultToLazy(() => ({ - error: { - code: 0, - message: String(error), - }, - })), - ) + })), + ) + } } diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts index 0c1f9d07a..26e184cef 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts @@ -8,7 +8,7 @@ import { CommandOptions, ExecOptions, ExecSpawnable, -} from "@start9labs/start-sdk/cjs/lib/util/SubContainer" +} from "@start9labs/start-sdk/package/lib/util/SubContainer" export const exec = promisify(cp.exec) export const execFile = promisify(cp.execFile) diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts index 91b0177eb..b6fe39854 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts @@ -2,11 +2,10 @@ import { polyfillEffects } from "./polyfillEffects" import { DockerProcedureContainer } from "./DockerProcedureContainer" import { SystemForEmbassy } from "." import { T, utils } from "@start9labs/start-sdk" -import { Daemon } from "@start9labs/start-sdk/cjs/lib/mainFn/Daemon" +import { Daemon } from "@start9labs/start-sdk/package/lib/mainFn/Daemon" import { Effects } from "../../../Models/Effects" import { off } from "node:process" -import { CommandController } from "@start9labs/start-sdk/cjs/lib/mainFn/CommandController" -import { asError } from "@start9labs/start-sdk/cjs/lib/util" +import { CommandController } from "@start9labs/start-sdk/package/lib/mainFn/CommandController" const EMBASSY_HEALTH_INTERVAL = 15 * 1000 const EMBASSY_PROPERTIES_LOOP = 30 * 1000 @@ -137,7 +136,7 @@ export class MainLoop { delete this.healthLoops await main?.daemon .stop() - .catch((e) => console.error(`Main loop error`, utils.asError(e))) + .catch((e: unknown) => console.error(`Main loop error`, utils.asError(e))) this.effects.setMainStatus({ status: "stopped" }) if (healthLoops) healthLoops.forEach((x) => clearInterval(x.interval)) } @@ -155,7 +154,7 @@ export class MainLoop { result: "starting", message: null, }) - .catch((e) => console.error(asError(e))) + .catch((e) => console.error(utils.asError(e))) const interval = setInterval(async () => { const actionProcedure = value const timeChanged = Date.now() - start diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts index 5b0380a1b..bed30b11a 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts @@ -2,8 +2,8 @@ import { ExtendedVersion, types as T, utils } from "@start9labs/start-sdk" import * as fs from "fs/promises" import { polyfillEffects } from "./polyfillEffects" -import { Duration, duration, fromDuration } from "../../../Models/Duration" -import { System, Procedure } from "../../../Interfaces/System" +import { fromDuration } from "../../../Models/Duration" +import { System } from "../../../Interfaces/System" import { matchManifest, Manifest } from "./matchManifest" import * as childProcess from "node:child_process" import { DockerProcedureContainer } from "./DockerProcedureContainer" @@ -27,19 +27,12 @@ import { Parser, array, } from "ts-matches" -import { JsonPath, unNestPath } from "../../../Models/JsonPath" -import { RpcResult, matchRpcResult } from "../../RpcListener" -import { CT } from "@start9labs/start-sdk" -import { - AddSslOptions, - BindOptions, -} from "@start9labs/start-sdk/cjs/lib/osBindings" +import { AddSslOptions } from "@start9labs/start-sdk/base/lib/osBindings" import { BindOptionsByProtocol, - Host, MultiHost, -} from "@start9labs/start-sdk/cjs/lib/interfaces/Host" -import { ServiceInterfaceBuilder } from "@start9labs/start-sdk/cjs/lib/interfaces/ServiceInterfaceBuilder" +} from "@start9labs/start-sdk/base/lib/interfaces/Host" +import { ServiceInterfaceBuilder } from "@start9labs/start-sdk/base/lib/interfaces/ServiceInterfaceBuilder" import { Effects } from "../../../Models/Effects" import { OldConfigSpec, @@ -48,18 +41,16 @@ import { transformNewConfigToOld, transformOldConfigToNew, } from "./transformConfigSpec" -import { MainEffects } from "@start9labs/start-sdk/cjs/lib/StartSdk" -import { StorePath } from "@start9labs/start-sdk/cjs/lib/store/PathBuilder" +import { partialDiff } from "@start9labs/start-sdk/base/lib/util" type Optional = A | undefined | null function todo(): never { throw new Error("Not implemented") } -const execFile = promisify(childProcess.execFile) const MANIFEST_LOCATION = "/usr/lib/startos/package/embassyManifest.json" export const EMBASSY_JS_LOCATION = "/usr/lib/startos/package/embassy.js" -const EMBASSY_POINTER_PATH_PREFIX = "/embassyConfig" as StorePath +const EMBASSY_POINTER_PATH_PREFIX = "/embassyConfig" as utils.StorePath const matchResult = object({ result: any, @@ -248,50 +239,21 @@ export class SystemForEmbassy implements System { readonly moduleCode: Partial, ) {} - async actionsMetadata(effects: T.Effects): Promise { - const actions = Object.entries(this.manifest.actions ?? {}) - return Promise.all( - actions.map(async ([actionId, action]): Promise => { - const name = action.name ?? actionId - const description = action.description - const warning = action.warning ?? null - const disabled = false - const input = (await convertToNewConfig(action["input-spec"] as any)) - .spec - const hasRunning = !!action["allowed-statuses"].find( - (x) => x === "running", - ) - const hasStopped = !!action["allowed-statuses"].find( - (x) => x === "stopped", - ) - // prettier-ignore - const allowedStatuses = - hasRunning && hasStopped ? "any": - hasRunning ? "onlyRunning" : - "onlyStopped" - - const group = null - return { - name, - description, - warning, - disabled, - allowedStatuses, - group, - input, - } - }), - ) + async containerInit(effects: Effects): Promise { + for (let depId in this.manifest.dependencies) { + if (this.manifest.dependencies[depId].config) { + await this.dependenciesAutoconfig(effects, depId, null) + } + } } - async containerInit(): Promise {} - async exit(): Promise { if (this.currentRunning) await this.currentRunning.clean() delete this.currentRunning } - async start(effects: MainEffects): Promise { + async start(effects: T.Effects): Promise { + effects.constRetry = utils.once(() => effects.restart()) if (!!this.currentRunning) return this.currentRunning = await MainLoop.of(this, effects) @@ -308,13 +270,18 @@ export class SystemForEmbassy implements System { } } - async packageInit( - effects: Effects, - previousVersion: Optional, - timeoutMs: number | null, - ): Promise { - if (previousVersion) - await this.migration(effects, previousVersion, timeoutMs) + async packageInit(effects: Effects, timeoutMs: number | null): Promise { + const previousVersion = await effects.getDataVersion() + if (previousVersion) { + if ( + (await this.migration(effects, previousVersion, timeoutMs)).configured + ) { + await effects.action.clearRequests({ only: ["needs-config"] }) + } + await effects.setDataVersion({ + version: ExtendedVersion.parseEmver(this.manifest.version).toString(), + }) + } await effects.setMainStatus({ status: "stopped" }) await this.exportActions(effects) await this.exportNetwork(effects) @@ -400,10 +367,57 @@ export class SystemForEmbassy implements System { ) } } + async getActionInput( + effects: Effects, + actionId: string, + timeoutMs: number | null, + ): Promise { + if (actionId === "config") { + const config = await this.getConfig(effects, timeoutMs) + return { spec: config.spec, value: config.config } + } else { + const oldSpec = this.manifest.actions?.[actionId]?.["input-spec"] + if (!oldSpec) return null + return { + spec: transformConfigSpec(oldSpec as OldConfigSpec), + value: null, + } + } + } + async runAction( + effects: Effects, + actionId: string, + input: unknown, + timeoutMs: number | null, + ): Promise { + if (actionId === "config") { + await this.setConfig(effects, input, timeoutMs) + return null + } else { + return this.action(effects, actionId, input, timeoutMs) + } + } async exportActions(effects: Effects) { const manifest = this.manifest - if (!manifest.actions) return - for (const [actionId, action] of Object.entries(manifest.actions)) { + const actions = { + ...manifest.actions, + } + if (manifest.config) { + actions.config = { + name: "Configure", + description: "Edit the configuration of this service", + "allowed-statuses": ["running", "stopped"], + "input-spec": {}, + implementation: { type: "script", args: [] }, + } + await effects.action.request({ + packageId: this.manifest.id, + actionId: "config", + replayId: "needs-config", + description: "This service must be configured before it can be run", + }) + } + for (const [actionId, action] of Object.entries(actions)) { const hasRunning = !!action["allowed-statuses"].find( (x) => x === "running", ) @@ -412,21 +426,22 @@ export class SystemForEmbassy implements System { ) // prettier-ignore const allowedStatuses = hasRunning && hasStopped ? "any": - hasRunning ? "onlyRunning" : - "onlyStopped" - await effects.exportAction({ + hasRunning ? "only-running" : + "only-stopped" + await effects.action.export({ id: actionId, metadata: { name: action.name, description: action.description, warning: action.warning || null, - input: action["input-spec"] as CT.InputSpec, - disabled: false, + visibility: "enabled", allowedStatuses, + hasInput: !!action["input-spec"], group: null, }, }) } + await effects.action.clear({ except: Object.keys(actions) }) } async packageUninit( effects: Effects, @@ -483,10 +498,7 @@ export class SystemForEmbassy implements System { await moduleCode.restoreBackup?.(polyfillEffects(effects, this.manifest)) } } - async getConfig( - effects: Effects, - timeoutMs: number | null, - ): Promise { + async getConfig(effects: Effects, timeoutMs: number | null) { return this.getConfigUncleaned(effects, timeoutMs).then(convertToNewConfig) } private async getConfigUncleaned( @@ -614,7 +626,7 @@ export class SystemForEmbassy implements System { effects: Effects, fromVersion: string, timeoutMs: number | null, - ): Promise { + ): Promise<{ configured: boolean }> { const fromEmver = ExtendedVersion.parseEmver(fromVersion) const currentEmver = ExtendedVersion.parseEmver(this.manifest.version) if (!this.manifest.migrations) return { configured: true } @@ -828,24 +840,44 @@ export class SystemForEmbassy implements System { async dependenciesAutoconfig( effects: Effects, id: string, - input: unknown, timeoutMs: number | null, ): Promise { - const oldConfig = object({ remoteConfig: any }).unsafeCast( - input, - ).remoteConfig // TODO: docker + const oldConfig = (await effects.store.get({ + packageId: id, + path: EMBASSY_POINTER_PATH_PREFIX, + callback: () => { + this.dependenciesAutoconfig(effects, id, timeoutMs) + }, + })) as U.Config const moduleCode = await this.moduleCode const method = moduleCode.dependencies?.[id]?.autoConfigure if (!method) return - return (await method( + const newConfig = (await method( polyfillEffects(effects, this.manifest), - oldConfig, + JSON.parse(JSON.stringify(oldConfig)), ).then((x) => { if ("result" in x) return x.result if ("error" in x) throw new Error("Error getting config: " + x.error) throw new Error("Error getting config: " + x["error-code"][1]) })) as any + const diff = partialDiff(oldConfig, newConfig) + if (diff) { + await effects.action.request({ + actionId: "config", + packageId: id, + replayId: `${id}/config`, + description: `Configure this dependency for the needs of ${this.manifest.title}`, + input: { + kind: "partial", + value: diff.diff, + }, + when: { + condition: "input-not-matches", + once: false, + }, + }) + } } } @@ -1020,9 +1052,7 @@ function extractServiceInterfaceId(manifest: Manifest, specInterface: string) { const serviceInterfaceId = `${specInterface}-${internalPort}` return serviceInterfaceId } -async function convertToNewConfig( - value: OldGetConfigRes, -): Promise { +async function convertToNewConfig(value: OldGetConfigRes) { const valueSpec: OldConfigSpec = matchOldConfigSpec.unsafeCast(value.spec) const spec = transformConfigSpec(valueSpec) if (!value.config) return { spec, config: null } diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/matchManifest.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/matchManifest.ts index bd8856b42..5bda20de0 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/matchManifest.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/matchManifest.ts @@ -42,6 +42,7 @@ const matchAction = object( export const matchManifest = object( { id: string, + title: string, version: string, main: matchDockerProcedure, assets: object( diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts index 675cb2e9a..7438070ea 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts @@ -105,12 +105,14 @@ export const polyfillEffects = ( args?: string[] | undefined timeoutMillis?: number | undefined }): Promise> { + const commands: [string, ...string[]] = [command, ...(args || [])] return startSdk .runCommand( effects, { id: manifest.main.image }, - [command, ...(args || [])], + commands, {}, + commands.join(" "), ) .then((x: any) => ({ stderr: x.stderr.toString(), @@ -154,11 +156,17 @@ export const polyfillEffects = ( path: string uid: string }): Promise { + const commands: [string, ...string[]] = [ + "chown", + "--recursive", + input.uid, + `/drive/${input.path}`, + ] await startSdk .runCommand( effects, { id: manifest.main.image }, - ["chown", "--recursive", input.uid, `/drive/${input.path}`], + commands, { mounts: [ { @@ -172,6 +180,7 @@ export const polyfillEffects = ( }, ], }, + commands.join(" "), ) .then((x: any) => ({ stderr: x.stderr.toString(), @@ -189,11 +198,17 @@ export const polyfillEffects = ( path: string mode: string }): Promise { + const commands: [string, ...string[]] = [ + "chmod", + "--recursive", + input.mode, + `/drive/${input.path}`, + ] await startSdk .runCommand( effects, { id: manifest.main.image }, - ["chmod", "--recursive", input.mode, `/drive/${input.path}`], + commands, { mounts: [ { @@ -207,6 +222,7 @@ export const polyfillEffects = ( }, ], }, + commands.join(" "), ) .then((x: any) => ({ stderr: x.stderr.toString(), diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/transformConfigSpec.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/transformConfigSpec.ts index 5ce601c57..4c074a1bd 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/transformConfigSpec.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/transformConfigSpec.ts @@ -1,4 +1,4 @@ -import { CT } from "@start9labs/start-sdk" +import { IST } from "@start9labs/start-sdk" import { dictionary, object, @@ -15,9 +15,9 @@ import { literal, } from "ts-matches" -export function transformConfigSpec(oldSpec: OldConfigSpec): CT.InputSpec { +export function transformConfigSpec(oldSpec: OldConfigSpec): IST.InputSpec { return Object.entries(oldSpec).reduce((inputSpec, [key, oldVal]) => { - let newVal: CT.ValueSpec + let newVal: IST.ValueSpec if (oldVal.type === "boolean") { newVal = { @@ -124,7 +124,7 @@ export function transformConfigSpec(oldSpec: OldConfigSpec): CT.InputSpec { spec: transformConfigSpec(matchOldConfigSpec.unsafeCast(spec)), }, }), - {} as Record, + {} as Record, ), disabled: false, required: true, @@ -141,7 +141,7 @@ export function transformConfigSpec(oldSpec: OldConfigSpec): CT.InputSpec { ...inputSpec, [key]: newVal, } - }, {} as CT.InputSpec) + }, {} as IST.InputSpec) } export function transformOldConfigToNew( @@ -233,10 +233,10 @@ export function transformNewConfigToOld( function getListSpec( oldVal: OldValueSpecList, -): CT.ValueSpecMultiselect | CT.ValueSpecList { +): IST.ValueSpecMultiselect | IST.ValueSpecList { const range = Range.from(oldVal.range) - let partial: Omit = { + let partial: Omit = { name: oldVal.name, description: oldVal.description || null, warning: oldVal.warning || null, diff --git a/container-runtime/src/Adapters/Systems/SystemForStartOs.ts b/container-runtime/src/Adapters/Systems/SystemForStartOs.ts index 51d91abb5..43096f0f0 100644 --- a/container-runtime/src/Adapters/Systems/SystemForStartOs.ts +++ b/container-runtime/src/Adapters/Systems/SystemForStartOs.ts @@ -6,16 +6,13 @@ import { RpcResult, matchRpcResult } from "../RpcListener" import { duration } from "../../Models/Duration" import { T, utils } from "@start9labs/start-sdk" import { Volume } from "../../Models/Volume" -import { MainEffects } from "@start9labs/start-sdk/cjs/lib/StartSdk" import { CallbackHolder } from "../../Models/CallbackHolder" import { Optional } from "ts-matches/lib/parsers/interfaces" export const STARTOS_JS_LOCATION = "/usr/lib/startos/package/index.js" type RunningMain = { - effects: MainEffects stop: () => Promise - callbacks: CallbackHolder } export class SystemForStartOs implements System { @@ -25,23 +22,24 @@ export class SystemForStartOs implements System { return new SystemForStartOs(require(STARTOS_JS_LOCATION)) } - constructor(readonly abi: T.ABI) {} - containerInit(): Promise { - throw new Error("Method not implemented.") + constructor(readonly abi: T.ABI) { + this + } + async containerInit(effects: Effects): Promise { + return void (await this.abi.containerInit({ effects })) } async packageInit( effects: Effects, - previousVersion: Optional = null, timeoutMs: number | null = null, ): Promise { - return void (await this.abi.init({ effects })) + return void (await this.abi.packageInit({ effects })) } async packageUninit( effects: Effects, nextVersion: Optional = null, timeoutMs: number | null = null, ): Promise { - return void (await this.abi.uninit({ effects, nextVersion })) + return void (await this.abi.packageUninit({ effects, nextVersion })) } async createBackup( effects: T.Effects, @@ -49,8 +47,6 @@ export class SystemForStartOs implements System { ): Promise { return void (await this.abi.createBackup({ effects, - pathMaker: ((options) => - new Volume(options.volume, options.path).path) as T.PathMaker, })) } async restoreBackup( @@ -59,80 +55,38 @@ export class SystemForStartOs implements System { ): Promise { return void (await this.abi.restoreBackup({ effects, - pathMaker: ((options) => - new Volume(options.volume, options.path).path) as T.PathMaker, })) } - getConfig( - effects: T.Effects, - timeoutMs: number | null, - ): Promise { - return this.abi.getConfig({ effects }) - } - async setConfig( - effects: Effects, - input: { effects: Effects; input: Record }, - timeoutMs: number | null, - ): Promise { - const _: unknown = await this.abi.setConfig({ effects, input }) - return - } - migration( - effects: Effects, - fromVersion: string, - timeoutMs: number | null, - ): Promise { - throw new Error("Method not implemented.") - } properties( effects: Effects, timeoutMs: number | null, ): Promise { throw new Error("Method not implemented.") } - async action( + getActionInput( effects: Effects, id: string, - formData: unknown, timeoutMs: number | null, - ): Promise { - const action = (await this.abi.actions({ effects }))[id] + ): Promise { + const action = this.abi.actions.get(id) if (!action) throw new Error(`Action ${id} not found`) - return action.run({ effects }) + return action.getInput({ effects }) } - dependenciesCheck( + runAction( effects: Effects, id: string, - oldConfig: unknown, + input: unknown, timeoutMs: number | null, - ): Promise { - const dependencyConfig = this.abi.dependencyConfig[id] - if (!dependencyConfig) throw new Error(`dependencyConfig ${id} not found`) - return dependencyConfig.query({ effects }) + ): Promise { + const action = this.abi.actions.get(id) + if (!action) throw new Error(`Action ${id} not found`) + return action.run({ effects, input }) } - async dependenciesAutoconfig( - effects: Effects, - id: string, - remoteConfig: unknown, - timeoutMs: number | null, - ): Promise { - const dependencyConfig = this.abi.dependencyConfig[id] - if (!dependencyConfig) throw new Error(`dependencyConfig ${id} not found`) - const queryResults = await this.getConfig(effects, timeoutMs) - return void (await dependencyConfig.update({ - queryResults, - remoteConfig, - })) // TODO - } - async actionsMetadata(effects: T.Effects): Promise { - return this.abi.actionsMetadata({ effects }) - } - - async init(): Promise {} async exit(): Promise {} - async start(effects: MainEffects): Promise { + async start(effects: Effects): Promise { + effects.constRetry = utils.once(() => effects.restart()) if (this.runningMain) await this.stop() let mainOnTerm: () => Promise | undefined const started = async (onTerm: () => Promise) => { @@ -141,36 +95,21 @@ export class SystemForStartOs implements System { } const daemons = await ( await this.abi.main({ - effects: effects as MainEffects, + effects, started, }) ).build() this.runningMain = { - effects, stop: async () => { if (mainOnTerm) await mainOnTerm() await daemons.term() }, - callbacks: new CallbackHolder(), - } - } - - callCallback(callback: number, args: any[]): void { - if (this.runningMain) { - this.runningMain.callbacks - .callCallback(callback, args) - .catch((error) => - console.error(`callback ${callback} failed`, utils.asError(error)), - ) - } else { - console.warn(`callback ${callback} ignored because system is not running`) } } async stop(): Promise { if (this.runningMain) { await this.runningMain.stop() - await this.runningMain.effects.clearCallbacks() this.runningMain = undefined } } diff --git a/container-runtime/src/Interfaces/AllGetDependencies.ts b/container-runtime/src/Interfaces/AllGetDependencies.ts index ca5c43585..24b68acc5 100644 --- a/container-runtime/src/Interfaces/AllGetDependencies.ts +++ b/container-runtime/src/Interfaces/AllGetDependencies.ts @@ -1,7 +1,4 @@ import { GetDependency } from "./GetDependency" import { System } from "./System" -import { MakeMainEffects, MakeProcedureEffects } from "./MakeEffects" -export type AllGetDependencies = GetDependency<"system", Promise> & - GetDependency<"makeProcedureEffects", MakeProcedureEffects> & - GetDependency<"makeMainEffects", MakeMainEffects> +export type AllGetDependencies = GetDependency<"system", Promise> diff --git a/container-runtime/src/Interfaces/MakeEffects.ts b/container-runtime/src/Interfaces/MakeEffects.ts deleted file mode 100644 index 3b25f8180..000000000 --- a/container-runtime/src/Interfaces/MakeEffects.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Effects } from "../Models/Effects" -import { MainEffects } from "@start9labs/start-sdk/cjs/lib/StartSdk" -export type MakeProcedureEffects = (procedureId: string) => Effects -export type MakeMainEffects = () => MainEffects diff --git a/container-runtime/src/Interfaces/System.ts b/container-runtime/src/Interfaces/System.ts index 1348b79e9..14ca002ca 100644 --- a/container-runtime/src/Interfaces/System.ts +++ b/container-runtime/src/Interfaces/System.ts @@ -1,39 +1,27 @@ import { types as T } from "@start9labs/start-sdk" -import { RpcResult } from "../Adapters/RpcListener" import { Effects } from "../Models/Effects" import { CallbackHolder } from "../Models/CallbackHolder" -import { MainEffects } from "@start9labs/start-sdk/cjs/lib/StartSdk" import { Optional } from "ts-matches/lib/parsers/interfaces" export type Procedure = - | "/init" - | "/uninit" - | "/config/set" - | "/config/get" + | "/packageInit" + | "/packageUninit" | "/backup/create" | "/backup/restore" - | "/actions/metadata" | "/properties" - | `/actions/${string}/get` + | `/actions/${string}/getInput` | `/actions/${string}/run` - | `/dependencies/${string}/query` - | `/dependencies/${string}/update` export type ExecuteResult = | { ok: unknown } | { err: { code: number; message: string } } export type System = { - containerInit(): Promise + containerInit(effects: T.Effects): Promise - start(effects: MainEffects): Promise - callCallback(callback: number, args: any[]): void + start(effects: T.Effects): Promise stop(): Promise - packageInit( - effects: Effects, - previousVersion: Optional, - timeoutMs: number | null, - ): Promise + packageInit(effects: Effects, timeoutMs: number | null): Promise packageUninit( effects: Effects, nextVersion: Optional, @@ -42,41 +30,21 @@ export type System = { createBackup(effects: T.Effects, timeoutMs: number | null): Promise restoreBackup(effects: T.Effects, timeoutMs: number | null): Promise - getConfig(effects: T.Effects, timeoutMs: number | null): Promise - setConfig( - effects: Effects, - input: { effects: Effects; input: Record }, - timeoutMs: number | null, - ): Promise - migration( - effects: Effects, - fromVersion: string, - timeoutMs: number | null, - ): Promise properties( effects: Effects, timeoutMs: number | null, ): Promise - action( + runAction( effects: Effects, actionId: string, - formData: unknown, + input: unknown, timeoutMs: number | null, - ): Promise - - dependenciesCheck( + ): Promise + getActionInput( effects: Effects, - id: string, - oldConfig: unknown, + actionId: string, timeoutMs: number | null, - ): Promise - dependenciesAutoconfig( - effects: Effects, - id: string, - oldConfig: unknown, - timeoutMs: number | null, - ): Promise - actionsMetadata(effects: T.Effects): Promise + ): Promise exit(): Promise } diff --git a/container-runtime/src/Models/CallbackHolder.ts b/container-runtime/src/Models/CallbackHolder.ts index b51af0bee..e1034b473 100644 --- a/container-runtime/src/Models/CallbackHolder.ts +++ b/container-runtime/src/Models/CallbackHolder.ts @@ -1,9 +1,22 @@ +import { T } from "@start9labs/start-sdk" + +const CallbackIdCell = { inc: 0 } + +const callbackRegistry = new FinalizationRegistry( + async (options: { cbs: Map; effects: T.Effects }) => { + await options.effects.clearCallbacks({ + only: Array.from(options.cbs.keys()), + }) + }, +) + export class CallbackHolder { - constructor() {} - private inc = 0 + constructor(private effects?: T.Effects) {} + private callbacks = new Map() + private children: WeakRef[] = [] private newId() { - return this.inc++ + return CallbackIdCell.inc++ } addCallback(callback?: Function) { if (!callback) { @@ -11,12 +24,38 @@ export class CallbackHolder { } const id = this.newId() this.callbacks.set(id, callback) + if (this.effects) + callbackRegistry.register(this, { + cbs: this.callbacks, + effects: this.effects, + }) return id } + child(): CallbackHolder { + const child = new CallbackHolder() + this.children.push(new WeakRef(child)) + return child + } + removeChild(child: CallbackHolder) { + this.children = this.children.filter((c) => { + const ref = c.deref() + return ref && ref !== child + }) + } + private getCallback(index: number): Function | undefined { + let callback = this.callbacks.get(index) + if (callback) this.callbacks.delete(index) + else { + for (let i = 0; i < this.children.length; i++) { + callback = this.children[i].deref()?.getCallback(index) + if (callback) return callback + } + } + return callback + } callCallback(index: number, args: any[]): Promise { - const callback = this.callbacks.get(index) - if (!callback) throw new Error(`Callback ${index} does not exist`) - this.callbacks.delete(index) + const callback = this.getCallback(index) + if (!callback) return Promise.resolve() return Promise.resolve().then(() => callback(...args)) } } diff --git a/container-runtime/src/Models/JsonPath.ts b/container-runtime/src/Models/JsonPath.ts index 95a2b3a00..a231adba7 100644 --- a/container-runtime/src/Models/JsonPath.ts +++ b/container-runtime/src/Models/JsonPath.ts @@ -1,9 +1,7 @@ import { literals, some, string } from "ts-matches" type NestedPath = `/${A}/${string}/${B}` -type NestedPaths = - | NestedPath<"actions", "run" | "get"> - | NestedPath<"dependencies", "query" | "update"> +type NestedPaths = NestedPath<"actions", "run" | "getInput"> // prettier-ignore type UnNestPaths = A extends `${infer A}/${infer B}` ? [...UnNestPaths, ... UnNestPaths] : @@ -15,21 +13,14 @@ export function unNestPath(a: A): UnNestPaths { function isNestedPath(path: string): path is NestedPaths { const paths = path.split("/") if (paths.length !== 4) return false - if (paths[1] === "actions" && (paths[3] === "run" || paths[3] === "get")) - return true - if ( - paths[1] === "dependencies" && - (paths[3] === "query" || paths[3] === "update") - ) + if (paths[1] === "actions" && (paths[3] === "run" || paths[3] === "getInput")) return true return false } export const jsonPath = some( literals( - "/init", - "/uninit", - "/config/set", - "/config/get", + "/packageInit", + "/packageUninit", "/backup/create", "/backup/restore", "/actions/metadata", diff --git a/container-runtime/src/index.ts b/container-runtime/src/index.ts index 5454bee3d..ec6a998f4 100644 --- a/container-runtime/src/index.ts +++ b/container-runtime/src/index.ts @@ -1,13 +1,10 @@ import { RpcListener } from "./Adapters/RpcListener" import { SystemForEmbassy } from "./Adapters/Systems/SystemForEmbassy" -import { makeMainEffects, makeProcedureEffects } from "./Adapters/EffectCreator" import { AllGetDependencies } from "./Interfaces/AllGetDependencies" import { getSystem } from "./Adapters/Systems" const getDependencies: AllGetDependencies = { system: getSystem, - makeProcedureEffects: () => makeProcedureEffects, - makeMainEffects: () => makeMainEffects, } new RpcListener(getDependencies) diff --git a/core/Cargo.lock b/core/Cargo.lock index ebbcf6b5d..c7eccf706 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -667,12 +667,24 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "bytemuck" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773d90827bc3feecfb67fab12e24de0749aad83c74b9504ecde46237b5cd24e2" + [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.7.1" @@ -2387,6 +2399,17 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "image" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10" +dependencies = [ + "bytemuck", + "byteorder-lite", + "num-traits", +] + [[package]] name = "imbl" version = "2.0.3" @@ -3813,6 +3836,15 @@ dependencies = [ "psl-types", ] +[[package]] +name = "qrcode" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d68782463e408eb1e668cf6152704bd856c78c5b6417adaee3203d8f4c1fc9ec" +dependencies = [ + "image", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -5090,6 +5122,7 @@ dependencies = [ "procfs", "proptest", "proptest-derive", + "qrcode", "rand 0.8.5", "regex", "reqwest", diff --git a/core/helpers/src/lib.rs b/core/helpers/src/lib.rs index 80631fea2..a9df58ece 100644 --- a/core/helpers/src/lib.rs +++ b/core/helpers/src/lib.rs @@ -6,6 +6,7 @@ use std::time::Duration; use color_eyre::eyre::{eyre, Context, Error}; use futures::future::BoxFuture; use futures::FutureExt; +use models::ResultExt; use tokio::fs::File; use tokio::sync::oneshot; use tokio::task::{JoinError, JoinHandle, LocalSet}; @@ -176,7 +177,7 @@ impl Drop for AtomicFile { if let Some(file) = self.file.take() { drop(file); let path = std::mem::take(&mut self.tmp_path); - tokio::spawn(async move { tokio::fs::remove_file(path).await.unwrap() }); + tokio::spawn(async move { tokio::fs::remove_file(path).await.log_err() }); } } } diff --git a/core/startos/src/util/clap.rs b/core/models/src/clap.rs similarity index 91% rename from core/startos/src/util/clap.rs rename to core/models/src/clap.rs index 7c3b5a0bc..122e8cf9d 100644 --- a/core/startos/src/util/clap.rs +++ b/core/models/src/clap.rs @@ -1,9 +1,8 @@ use std::marker::PhantomData; use std::str::FromStr; -use clap::builder::TypedValueParser; - -use crate::prelude::*; +use rpc_toolkit::clap; +use rpc_toolkit::clap::builder::TypedValueParser; pub struct FromStrParser(PhantomData); impl FromStrParser { diff --git a/core/models/src/id/mod.rs b/core/models/src/id/mod.rs index 85c9d8255..90dbc978c 100644 --- a/core/models/src/id/mod.rs +++ b/core/models/src/id/mod.rs @@ -11,6 +11,7 @@ mod host; mod image; mod invalid_id; mod package; +mod replay; mod service_interface; mod volume; @@ -20,6 +21,7 @@ pub use host::HostId; pub use image::ImageId; pub use invalid_id::InvalidId; pub use package::{PackageId, SYSTEM_PACKAGE_ID}; +pub use replay::ReplayId; pub use service_interface::ServiceInterfaceId; pub use volume::VolumeId; diff --git a/core/models/src/id/replay.rs b/core/models/src/id/replay.rs new file mode 100644 index 000000000..299b6160a --- /dev/null +++ b/core/models/src/id/replay.rs @@ -0,0 +1,45 @@ +use std::convert::Infallible; +use std::path::Path; +use std::str::FromStr; + +use serde::{Deserialize, Serialize}; +use ts_rs::TS; +use yasi::InternedString; + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, TS)] +#[ts(type = "string")] +pub struct ReplayId(InternedString); +impl FromStr for ReplayId { + type Err = Infallible; + fn from_str(s: &str) -> Result { + Ok(ReplayId(InternedString::intern(s))) + } +} +impl AsRef for ReplayId { + fn as_ref(&self) -> &ReplayId { + self + } +} +impl std::fmt::Display for ReplayId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", &self.0) + } +} +impl AsRef for ReplayId { + fn as_ref(&self) -> &str { + self.0.as_ref() + } +} +impl AsRef for ReplayId { + fn as_ref(&self) -> &Path { + self.0.as_ref() + } +} +impl<'de> Deserialize<'de> for ReplayId { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + Ok(ReplayId(serde::Deserialize::deserialize(deserializer)?)) + } +} diff --git a/core/models/src/id/service_interface.rs b/core/models/src/id/service_interface.rs index 25aec0aba..f08d89cd5 100644 --- a/core/models/src/id/service_interface.rs +++ b/core/models/src/id/service_interface.rs @@ -1,9 +1,11 @@ use std::path::Path; +use std::str::FromStr; +use rpc_toolkit::clap::builder::ValueParserFactory; use serde::{Deserialize, Deserializer, Serialize}; use ts_rs::TS; -use crate::Id; +use crate::{FromStrParser, Id}; #[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, TS)] #[ts(export, type = "string")] @@ -59,3 +61,15 @@ impl sqlx::Type for ServiceInterfaceId { <&str as sqlx::Type>::compatible(ty) } } +impl FromStr for ServiceInterfaceId { + type Err = ::Err; + fn from_str(s: &str) -> Result { + Id::from_str(s).map(Self) + } +} +impl ValueParserFactory for ServiceInterfaceId { + type Parser = FromStrParser; + fn value_parser() -> Self::Parser { + FromStrParser::new() + } +} diff --git a/core/models/src/lib.rs b/core/models/src/lib.rs index ad9055f24..304ac87c5 100644 --- a/core/models/src/lib.rs +++ b/core/models/src/lib.rs @@ -1,3 +1,4 @@ +mod clap; mod data_url; mod errors; mod id; @@ -5,6 +6,7 @@ mod mime; mod procedure_name; mod version; +pub use clap::*; pub use data_url::*; pub use errors::*; pub use id::*; diff --git a/core/models/src/procedure_name.rs b/core/models/src/procedure_name.rs index 466835818..9f8a6c0dd 100644 --- a/core/models/src/procedure_name.rs +++ b/core/models/src/procedure_name.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::{ActionId, PackageId}; +use crate::ActionId; #[derive(Debug, Clone, Serialize, Deserialize)] pub enum ProcedureName { @@ -9,30 +9,24 @@ pub enum ProcedureName { CreateBackup, Properties, RestoreBackup, - ActionMetadata, + GetActionInput(ActionId), RunAction(ActionId), - GetAction(ActionId), - QueryDependency(PackageId), - UpdateDependency(PackageId), - Init, - Uninit, + PackageInit, + PackageUninit, } impl ProcedureName { pub fn js_function_name(&self) -> String { match self { - ProcedureName::Init => "/init".to_string(), - ProcedureName::Uninit => "/uninit".to_string(), + ProcedureName::PackageInit => "/packageInit".to_string(), + ProcedureName::PackageUninit => "/packageUninit".to_string(), ProcedureName::SetConfig => "/config/set".to_string(), ProcedureName::GetConfig => "/config/get".to_string(), ProcedureName::CreateBackup => "/backup/create".to_string(), ProcedureName::Properties => "/properties".to_string(), ProcedureName::RestoreBackup => "/backup/restore".to_string(), - ProcedureName::ActionMetadata => "/actions/metadata".to_string(), ProcedureName::RunAction(id) => format!("/actions/{}/run", id), - ProcedureName::GetAction(id) => format!("/actions/{}/get", id), - ProcedureName::QueryDependency(id) => format!("/dependencies/{}/query", id), - ProcedureName::UpdateDependency(id) => format!("/dependencies/{}/update", id), + ProcedureName::GetActionInput(id) => format!("/actions/{}/getInput", id), } } } diff --git a/core/startos/Cargo.toml b/core/startos/Cargo.toml index f2b780b7b..19d0df244 100644 --- a/core/startos/Cargo.toml +++ b/core/startos/Cargo.toml @@ -156,6 +156,7 @@ prettytable-rs = "0.10.0" procfs = { version = "0.16.0", optional = true } proptest = "1.3.1" proptest-derive = "0.5.0" +qrcode = "0.14.1" rand = { version = "0.8.5", features = ["std"] } regex = "1.10.2" reqwest = { version = "0.12.4", features = ["stream", "json", "socks"] } diff --git a/core/startos/src/action.rs b/core/startos/src/action.rs index 7c4492adc..f7e17f564 100644 --- a/core/startos/src/action.rs +++ b/core/startos/src/action.rs @@ -1,15 +1,76 @@ -use clap::Parser; +use std::fmt; + +use clap::{CommandFactory, FromArgMatches, Parser}; pub use models::ActionId; use models::PackageId; +use qrcode::QrCode; +use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; use tracing::instrument; use ts_rs::TS; -use crate::config::Config; -use crate::context::RpcContext; +use crate::context::{CliContext, RpcContext}; use crate::prelude::*; use crate::rpc_continuations::Guid; -use crate::util::serde::{display_serializable, StdinDeserializable, WithIoFormat}; +use crate::util::serde::{ + display_serializable, HandlerExtSerde, StdinDeserializable, WithIoFormat, +}; + +pub fn action_api() -> ParentHandler { + ParentHandler::new() + .subcommand( + "get-input", + from_fn_async(get_action_input) + .with_display_serializable() + .with_call_remote::(), + ) + .subcommand( + "run", + from_fn_async(run_action) + .with_display_serializable() + .with_custom_display_fn(|_, res| { + if let Some(res) = res { + println!("{res}") + } + Ok(()) + }) + .with_call_remote::(), + ) +} + +#[derive(Debug, Clone, Deserialize, Serialize, TS)] +#[ts(export)] +#[serde(rename_all = "camelCase")] +pub struct ActionInput { + #[ts(type = "Record")] + pub spec: Value, + #[ts(type = "Record | null")] + pub value: Option, +} + +#[derive(Deserialize, Serialize, TS, Parser)] +#[serde(rename_all = "camelCase")] +pub struct GetActionInputParams { + pub package_id: PackageId, + pub action_id: ActionId, +} + +#[instrument(skip_all)] +pub async fn get_action_input( + ctx: RpcContext, + GetActionInputParams { + package_id, + action_id, + }: GetActionInputParams, +) -> Result, Error> { + ctx.services + .get(&package_id) + .await + .as_ref() + .or_not_found(lazy_format!("Manager for {}", package_id))? + .get_action_input(Guid::new(), action_id) + .await +} #[derive(Debug, Serialize, Deserialize)] #[serde(tag = "version")] @@ -17,6 +78,13 @@ pub enum ActionResult { #[serde(rename = "0")] V0(ActionResultV0), } +impl fmt::Display for ActionResult { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::V0(res) => res.fmt(f), + } + } +} #[derive(Debug, Serialize, Deserialize)] pub struct ActionResultV0 { @@ -25,63 +93,111 @@ pub struct ActionResultV0 { pub copyable: bool, pub qr: bool, } - -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub enum DockerStatus { - Running, - Stopped, +impl fmt::Display for ActionResultV0 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.message)?; + if let Some(value) = &self.value { + write!(f, ":\n{value}")?; + if self.qr { + use qrcode::render::unicode; + write!( + f, + "\n{}", + QrCode::new(value.as_bytes()) + .unwrap() + .render::() + .build() + )?; + } + } + Ok(()) + } } -pub fn display_action_result(params: WithIoFormat, result: ActionResult) { +pub fn display_action_result(params: WithIoFormat, result: Option) { + let Some(result) = result else { + return; + }; if let Some(format) = params.format { return display_serializable(format, result); } - match result { - ActionResult::V0(ar) => { - println!( - "{}: {}", - ar.message, - serde_json::to_string(&ar.value).unwrap() - ); - } - } + println!("{result}") } -#[derive(Deserialize, Serialize, Parser, TS)] +#[derive(Deserialize, Serialize, TS)] #[serde(rename_all = "camelCase")] -#[command(rename_all = "kebab-case")] -pub struct ActionParams { - #[arg(id = "id")] - #[serde(rename = "id")] +pub struct RunActionParams { + pub package_id: PackageId, + pub action_id: ActionId, + #[ts(optional, type = "any")] + pub input: Option, +} + +#[derive(Parser)] +struct CliRunActionParams { pub package_id: PackageId, pub action_id: ActionId, #[command(flatten)] - #[ts(type = "{ [key: string]: any } | null")] - #[serde(default)] - pub input: StdinDeserializable>, + pub input: StdinDeserializable>, +} +impl From for RunActionParams { + fn from( + CliRunActionParams { + package_id, + action_id, + input, + }: CliRunActionParams, + ) -> Self { + Self { + package_id, + action_id, + input: input.0, + } + } +} +impl CommandFactory for RunActionParams { + fn command() -> clap::Command { + CliRunActionParams::command() + } + fn command_for_update() -> clap::Command { + CliRunActionParams::command_for_update() + } +} +impl FromArgMatches for RunActionParams { + fn from_arg_matches(matches: &clap::ArgMatches) -> Result { + CliRunActionParams::from_arg_matches(matches).map(Self::from) + } + fn from_arg_matches_mut(matches: &mut clap::ArgMatches) -> Result { + CliRunActionParams::from_arg_matches_mut(matches).map(Self::from) + } + fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> Result<(), clap::Error> { + *self = CliRunActionParams::from_arg_matches(matches).map(Self::from)?; + Ok(()) + } + fn update_from_arg_matches_mut( + &mut self, + matches: &mut clap::ArgMatches, + ) -> Result<(), clap::Error> { + *self = CliRunActionParams::from_arg_matches_mut(matches).map(Self::from)?; + Ok(()) + } } -// impl C // #[command(about = "Executes an action", display(display_action_result))] #[instrument(skip_all)] -pub async fn action( +pub async fn run_action( ctx: RpcContext, - ActionParams { + RunActionParams { package_id, action_id, - input: StdinDeserializable(input), - }: ActionParams, -) -> Result { + input, + }: RunActionParams, +) -> Result, Error> { ctx.services .get(&package_id) .await .as_ref() .or_not_found(lazy_format!("Manager for {}", package_id))? - .action( - Guid::new(), - action_id, - input.map(|c| to_value(&c)).transpose()?.unwrap_or_default(), - ) + .run_action(Guid::new(), action_id, input.unwrap_or_default()) .await } diff --git a/core/startos/src/backup/backup_bulk.rs b/core/startos/src/backup/backup_bulk.rs index b4419e88e..5defebe5d 100644 --- a/core/startos/src/backup/backup_bulk.rs +++ b/core/startos/src/backup/backup_bulk.rs @@ -141,7 +141,7 @@ impl Drop for BackupStatusGuard { .ser(&None) }) .await - .unwrap() + .log_err() }); } } diff --git a/core/startos/src/backup/target/mod.rs b/core/startos/src/backup/target/mod.rs index 032f70848..fcb291005 100644 --- a/core/startos/src/backup/target/mod.rs +++ b/core/startos/src/backup/target/mod.rs @@ -9,7 +9,7 @@ use digest::generic_array::GenericArray; use digest::OutputSizeUser; use exver::Version; use imbl_value::InternedString; -use models::PackageId; +use models::{FromStrParser, PackageId}; use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; use sha2::Sha256; @@ -27,7 +27,6 @@ use crate::disk::mount::filesystem::{FileSystem, MountType, ReadWrite}; use crate::disk::mount::guard::{GenericMountGuard, TmpMountGuard}; use crate::disk::util::PartitionInfo; use crate::prelude::*; -use crate::util::clap::FromStrParser; use crate::util::serde::{ deserialize_from_str, display_serializable, serialize_display, HandlerExtSerde, WithIoFormat, }; diff --git a/core/startos/src/config/action.rs b/core/startos/src/config/action.rs deleted file mode 100644 index ef26571a7..000000000 --- a/core/startos/src/config/action.rs +++ /dev/null @@ -1,22 +0,0 @@ -use std::collections::{BTreeMap, BTreeSet}; - -use models::PackageId; -use serde::{Deserialize, Serialize}; - -use super::{Config, ConfigSpec}; -#[allow(unused_imports)] -use crate::prelude::*; -use crate::status::health_check::HealthCheckId; - -#[derive(Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct ConfigRes { - pub config: Option, - pub spec: ConfigSpec, -} - -#[derive(Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct SetResult { - pub depends_on: BTreeMap>, -} diff --git a/core/startos/src/config/mod.rs b/core/startos/src/config/mod.rs deleted file mode 100644 index 22edd98f7..000000000 --- a/core/startos/src/config/mod.rs +++ /dev/null @@ -1,281 +0,0 @@ -use std::collections::BTreeSet; -use std::sync::Arc; -use std::time::Duration; - -use clap::Parser; -use color_eyre::eyre::eyre; -use indexmap::{IndexMap, IndexSet}; -use itertools::Itertools; -use models::{ErrorKind, OptionExt, PackageId}; -use patch_db::value::InternedString; -use patch_db::Value; -use regex::Regex; -use rpc_toolkit::{from_fn_async, Context, Empty, HandlerExt, ParentHandler}; -use serde::{Deserialize, Serialize}; -use tracing::instrument; -use ts_rs::TS; - -use crate::context::{CliContext, RpcContext}; -use crate::prelude::*; -use crate::rpc_continuations::Guid; -use crate::util::serde::{HandlerExtSerde, StdinDeserializable}; - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct ConfigSpec(pub IndexMap); - -pub mod action; -pub mod util; - -use util::NumRange; - -use self::action::ConfigRes; - -pub type Config = patch_db::value::InOMap; -pub trait TypeOf { - fn type_of(&self) -> &'static str; -} -impl TypeOf for Value { - fn type_of(&self) -> &'static str { - match self { - Value::Array(_) => "list", - Value::Bool(_) => "boolean", - Value::Null => "null", - Value::Number(_) => "number", - Value::Object(_) => "object", - Value::String(_) => "string", - } - } -} - -#[derive(Debug, thiserror::Error)] -pub enum ConfigurationError { - #[error("Timeout Error")] - TimeoutError(#[from] TimeoutError), - #[error("No Match: {0}")] - NoMatch(#[from] NoMatchWithPath), - #[error("System Error: {0}")] - SystemError(Error), -} -impl From for Error { - fn from(err: ConfigurationError) -> Self { - let kind = match &err { - ConfigurationError::SystemError(e) => e.kind, - _ => crate::ErrorKind::ConfigGen, - }; - crate::Error::new(err, kind) - } -} - -#[derive(Clone, Copy, Debug, thiserror::Error)] -#[error("Timeout Error")] -pub struct TimeoutError; - -#[derive(Clone, Debug, thiserror::Error)] -pub struct NoMatchWithPath { - pub path: Vec, - pub error: MatchError, -} -impl NoMatchWithPath { - pub fn new(error: MatchError) -> Self { - NoMatchWithPath { - path: Vec::new(), - error, - } - } - pub fn prepend(mut self, seg: InternedString) -> Self { - self.path.push(seg); - self - } -} -impl std::fmt::Display for NoMatchWithPath { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}: {}", self.path.iter().rev().join("."), self.error) - } -} -impl From for Error { - fn from(e: NoMatchWithPath) -> Self { - ConfigurationError::from(e).into() - } -} - -#[derive(Clone, Debug, thiserror::Error)] -pub enum MatchError { - #[error("String {0:?} Does Not Match Pattern {1}")] - Pattern(Arc, Regex), - #[error("String {0:?} Is Not In Enum {1:?}")] - Enum(Arc, IndexSet), - #[error("Field Is Not Nullable")] - NotNullable, - #[error("Length Mismatch: expected {0}, actual: {1}")] - LengthMismatch(NumRange, usize), - #[error("Invalid Type: expected {0}, actual: {1}")] - InvalidType(&'static str, &'static str), - #[error("Number Out Of Range: expected {0}, actual: {1}")] - OutOfRange(NumRange, f64), - #[error("Number Is Not Integral: {0}")] - NonIntegral(f64), - #[error("Variant {0:?} Is Not In Union {1:?}")] - Union(Arc, IndexSet), - #[error("Variant Is Missing Tag {0:?}")] - MissingTag(InternedString), - #[error("Property {0:?} Of Variant {1:?} Conflicts With Union Tag")] - PropertyMatchesUnionTag(InternedString, String), - #[error("Name of Property {0:?} Conflicts With Map Tag Name")] - PropertyNameMatchesMapTag(String), - #[error("Object Key Is Invalid: {0}")] - InvalidKey(String), - #[error("Value In List Is Not Unique")] - ListUniquenessViolation, -} - -#[derive(Deserialize, Serialize, Parser, TS)] -#[serde(rename_all = "camelCase")] -#[command(rename_all = "kebab-case")] -pub struct ConfigParams { - pub id: PackageId, -} - -// #[command(subcommands(get, set))] -pub fn config() -> ParentHandler { - ParentHandler::new() - .subcommand( - "get", - from_fn_async(get) - .with_inherited(|ConfigParams { id }, _| id) - .with_display_serializable() - .with_call_remote::(), - ) - .subcommand( - "set", - set::().with_inherited(|ConfigParams { id }, _| id), - ) -} - -#[instrument(skip_all)] -pub async fn get(ctx: RpcContext, _: Empty, id: PackageId) -> Result { - ctx.services - .get(&id) - .await - .as_ref() - .or_not_found(lazy_format!("Manager for {id}"))? - .get_config(Guid::new()) - .await -} - -#[derive(Deserialize, Serialize, Parser, TS)] -#[serde(rename_all = "camelCase")] -pub struct SetParams { - #[arg(long = "timeout")] - pub timeout: Option, - #[command(flatten)] - #[ts(type = "{ [key: string]: any } | null")] - pub config: StdinDeserializable>, -} - -// #[command( -// subcommands(self(set_impl(async, context(RpcContext))), set_dry), -// display(display_none), -// metadata(sync_db = true) -// )] -#[instrument(skip_all)] -pub fn set() -> ParentHandler { - ParentHandler::new() - .root_handler( - from_fn_async(set_impl) - .with_metadata("sync_db", Value::Bool(true)) - .with_inherited(|set_params, id| (id, set_params)) - .no_display() - .with_call_remote::(), - ) - .subcommand( - "dry", - from_fn_async(set_dry) - .with_inherited(|set_params, id| (id, set_params)) - .no_display() - .with_call_remote::(), - ) -} - -pub async fn set_dry( - ctx: RpcContext, - _: Empty, - ( - id, - SetParams { - timeout, - config: StdinDeserializable(config), - }, - ): (PackageId, SetParams), -) -> Result, Error> { - let mut breakages = BTreeSet::new(); - - let procedure_id = Guid::new(); - - let db = ctx.db.peek().await; - for dep in db - .as_public() - .as_package_data() - .as_entries()? - .into_iter() - .filter_map( - |(k, v)| match v.as_current_dependencies().contains_key(&id) { - Ok(true) => Some(Ok(k)), - Ok(false) => None, - Err(e) => Some(Err(e)), - }, - ) - { - let dep_id = dep?; - - let Some(dependent) = &*ctx.services.get(&dep_id).await else { - continue; - }; - - if dependent - .dependency_config(procedure_id.clone(), id.clone(), config.clone()) - .await? - .is_some() - { - breakages.insert(dep_id); - } - } - - Ok(breakages) -} - -#[derive(Default)] -pub struct ConfigureContext { - pub timeout: Option, - pub config: Option, -} - -#[instrument(skip_all)] -pub async fn set_impl( - ctx: RpcContext, - _: Empty, - ( - id, - SetParams { - timeout, - config: StdinDeserializable(config), - }, - ): (PackageId, SetParams), -) -> Result<(), Error> { - let configure_context = ConfigureContext { - timeout: timeout.map(|t| *t), - config, - }; - ctx.services - .get(&id) - .await - .as_ref() - .ok_or_else(|| { - Error::new( - eyre!("There is no manager running for {id}"), - ErrorKind::Unknown, - ) - })? - .configure(Guid::new(), configure_context) - .await?; - Ok(()) -} diff --git a/core/startos/src/config/util.rs b/core/startos/src/config/util.rs deleted file mode 100644 index 359c24476..000000000 --- a/core/startos/src/config/util.rs +++ /dev/null @@ -1,406 +0,0 @@ -use std::borrow::Cow; -use std::ops::{Bound, RangeBounds, RangeInclusive}; - -use patch_db::Value; -use rand::distributions::Distribution; -use rand::Rng; - -use super::Config; - -pub const STATIC_NULL: Value = Value::Null; - -#[derive(Clone, Debug)] -pub struct CharSet(pub Vec<(RangeInclusive, usize)>, usize); -impl CharSet { - pub fn contains(&self, c: &char) -> bool { - self.0.iter().any(|r| r.0.contains(c)) - } - pub fn gen(&self, rng: &mut R) -> char { - let mut idx = rng.gen_range(0..self.1); - for r in &self.0 { - if idx < r.1 { - return std::convert::TryFrom::try_from( - rand::distributions::Uniform::new_inclusive( - u32::from(*r.0.start()), - u32::from(*r.0.end()), - ) - .sample(rng), - ) - .unwrap(); - } else { - idx -= r.1; - } - } - unreachable!() - } -} -impl Default for CharSet { - fn default() -> Self { - CharSet(vec![('!'..='~', 94)], 94) - } -} -impl<'de> serde::de::Deserialize<'de> for CharSet { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - let mut res = Vec::new(); - let mut len = 0; - let mut a: Option = None; - let mut b: Option = None; - let mut in_range = false; - for c in s.chars() { - match c { - ',' => match (a, b, in_range) { - (Some(start), Some(end), _) => { - if !end.is_ascii() { - return Err(serde::de::Error::custom("Invalid Character")); - } - if start >= end { - return Err(serde::de::Error::custom("Invalid Bounds")); - } - let l = u32::from(end) - u32::from(start) + 1; - res.push((start..=end, l as usize)); - len += l as usize; - a = None; - b = None; - in_range = false; - } - (Some(start), None, false) => { - len += 1; - res.push((start..=start, 1)); - a = None; - } - (Some(_), None, true) => { - b = Some(','); - } - (None, None, false) => { - a = Some(','); - } - _ => { - return Err(serde::de::Error::custom("Syntax Error")); - } - }, - '-' => { - if a.is_none() { - a = Some('-'); - } else if !in_range { - in_range = true; - } else if b.is_none() { - b = Some('-') - } else { - return Err(serde::de::Error::custom("Syntax Error")); - } - } - _ => { - if a.is_none() { - a = Some(c); - } else if in_range && b.is_none() { - b = Some(c); - } else { - return Err(serde::de::Error::custom("Syntax Error")); - } - } - } - } - match (a, b) { - (Some(start), Some(end)) => { - if !end.is_ascii() { - return Err(serde::de::Error::custom("Invalid Character")); - } - if start >= end { - return Err(serde::de::Error::custom("Invalid Bounds")); - } - let l = u32::from(end) - u32::from(start) + 1; - res.push((start..=end, l as usize)); - len += l as usize; - } - (Some(c), None) => { - len += 1; - res.push((c..=c, 1)); - } - _ => (), - } - - Ok(CharSet(res, len)) - } -} -impl serde::ser::Serialize for CharSet { - fn serialize(&self, serializer: S) -> Result - where - S: serde::ser::Serializer, - { - <&str>::serialize( - &self - .0 - .iter() - .map(|r| match r.1 { - 1 => format!("{}", r.0.start()), - _ => format!("{}-{}", r.0.start(), r.0.end()), - }) - .collect::>() - .join(",") - .as_str(), - serializer, - ) - } -} - -pub trait MergeWith { - fn merge_with(&mut self, other: &serde_json::Value); -} - -impl MergeWith for serde_json::Value { - fn merge_with(&mut self, other: &serde_json::Value) { - use serde_json::Value::Object; - if let (Object(orig), Object(ref other)) = (self, other) { - for (key, val) in other.into_iter() { - match (orig.get_mut(key), val) { - (Some(new_orig @ Object(_)), other @ Object(_)) => { - new_orig.merge_with(other); - } - (None, _) => { - orig.insert(key.clone(), val.clone()); - } - _ => (), - } - } - } - } -} - -#[test] -fn merge_with_tests() { - use serde_json::json; - - let mut a = json!( - {"a": 1, "c": {"d": "123"}, "i": [1,2,3], "j": {}, "k":[1,2,3], "l": "test"} - ); - a.merge_with( - &json!({"a":"a", "b": "b", "c":{"d":"d", "e":"e"}, "f":{"g":"g"}, "h": [1,2,3], "i":"i", "j":[1,2,3], "k":{}}), - ); - assert_eq!( - a, - json!({"a": 1, "c": {"d": "123", "e":"e"}, "b":"b", "f": {"g":"g"}, "h":[1,2,3], "i":[1,2,3], "j": {}, "k":[1,2,3], "l": "test"}) - ) -} -pub mod serde_regex { - use regex::Regex; - use serde::*; - - pub fn serialize(regex: &Regex, serializer: S) -> Result - where - S: Serializer, - { - <&str>::serialize(®ex.as_str(), serializer) - } - - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - Regex::new(&s).map_err(|e| de::Error::custom(e)) - } -} - -#[derive(Clone, Debug)] -pub struct NumRange( - pub (Bound, Bound), -); -impl std::ops::Deref for NumRange -where - T: std::str::FromStr + std::fmt::Display + std::cmp::PartialOrd, -{ - type Target = (Bound, Bound); - - fn deref(&self) -> &Self::Target { - &self.0 - } -} -impl<'de, T> serde::de::Deserialize<'de> for NumRange -where - T: std::str::FromStr + std::fmt::Display + std::cmp::PartialOrd, - ::Err: std::fmt::Display, -{ - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - let mut split = s.split(","); - let start = split - .next() - .map(|s| match s.get(..1) { - Some("(") => match s.get(1..2) { - Some("*") => Ok(Bound::Unbounded), - _ => s[1..] - .trim() - .parse() - .map(Bound::Excluded) - .map_err(|e| serde::de::Error::custom(e)), - }, - Some("[") => s[1..] - .trim() - .parse() - .map(Bound::Included) - .map_err(|e| serde::de::Error::custom(e)), - _ => Err(serde::de::Error::custom(format!( - "Could not parse left bound: {}", - s - ))), - }) - .transpose()? - .unwrap(); - let end = split - .next() - .map(|s| match s.get(s.len() - 1..) { - Some(")") => match s.get(s.len() - 2..s.len() - 1) { - Some("*") => Ok(Bound::Unbounded), - _ => s[..s.len() - 1] - .trim() - .parse() - .map(Bound::Excluded) - .map_err(|e| serde::de::Error::custom(e)), - }, - Some("]") => s[..s.len() - 1] - .trim() - .parse() - .map(Bound::Included) - .map_err(|e| serde::de::Error::custom(e)), - _ => Err(serde::de::Error::custom(format!( - "Could not parse right bound: {}", - s - ))), - }) - .transpose()? - .unwrap_or(Bound::Unbounded); - - Ok(NumRange((start, end))) - } -} -impl std::fmt::Display for NumRange -where - T: std::str::FromStr + std::fmt::Display + std::cmp::PartialOrd, -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self.start_bound() { - Bound::Excluded(n) => write!(f, "({},", n)?, - Bound::Included(n) => write!(f, "[{},", n)?, - Bound::Unbounded => write!(f, "(*,")?, - }; - match self.end_bound() { - Bound::Excluded(n) => write!(f, "{})", n), - Bound::Included(n) => write!(f, "{}]", n), - Bound::Unbounded => write!(f, "*)"), - } - } -} -impl serde::ser::Serialize for NumRange -where - T: std::str::FromStr + std::fmt::Display + std::cmp::PartialOrd, -{ - fn serialize(&self, serializer: S) -> Result - where - S: serde::ser::Serializer, - { - <&str>::serialize(&format!("{}", self).as_str(), serializer) - } -} - -#[derive(Clone, Debug)] -pub enum UniqueBy { - Any(Vec), - All(Vec), - Exactly(String), - NotUnique, -} -impl UniqueBy { - pub fn eq(&self, lhs: &Config, rhs: &Config) -> bool { - match self { - UniqueBy::Any(any) => any.iter().any(|u| u.eq(lhs, rhs)), - UniqueBy::All(all) => all.iter().all(|u| u.eq(lhs, rhs)), - UniqueBy::Exactly(key) => lhs.get(&**key) == rhs.get(&**key), - UniqueBy::NotUnique => false, - } - } -} -impl Default for UniqueBy { - fn default() -> Self { - UniqueBy::NotUnique - } -} -impl<'de> serde::de::Deserialize<'de> for UniqueBy { - fn deserialize>(deserializer: D) -> Result { - struct Visitor; - impl<'de> serde::de::Visitor<'de> for Visitor { - type Value = UniqueBy; - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(formatter, "a key, an \"any\" object, or an \"all\" object") - } - fn visit_str(self, v: &str) -> Result { - Ok(UniqueBy::Exactly(v.to_owned())) - } - fn visit_string(self, v: String) -> Result { - Ok(UniqueBy::Exactly(v)) - } - fn visit_map>( - self, - mut map: A, - ) -> Result { - let mut variant = None; - while let Some(key) = map.next_key::>()? { - match key.as_ref() { - "any" => { - return Ok(UniqueBy::Any(map.next_value()?)); - } - "all" => { - return Ok(UniqueBy::All(map.next_value()?)); - } - _ => { - variant = Some(key); - } - } - } - Err(serde::de::Error::unknown_variant( - variant.unwrap_or_default().as_ref(), - &["any", "all"], - )) - } - fn visit_unit(self) -> Result { - Ok(UniqueBy::NotUnique) - } - fn visit_none(self) -> Result { - Ok(UniqueBy::NotUnique) - } - } - deserializer.deserialize_any(Visitor) - } -} - -impl serde::ser::Serialize for UniqueBy { - fn serialize(&self, serializer: S) -> Result - where - S: serde::ser::Serializer, - { - use serde::ser::SerializeMap; - - match self { - UniqueBy::Any(any) => { - let mut map = serializer.serialize_map(Some(1))?; - map.serialize_key("any")?; - map.serialize_value(any)?; - map.end() - } - UniqueBy::All(all) => { - let mut map = serializer.serialize_map(Some(1))?; - map.serialize_key("all")?; - map.serialize_value(all)?; - map.end() - } - UniqueBy::Exactly(key) => serializer.serialize_str(key), - UniqueBy::NotUnique => serializer.serialize_unit(), - } - } -} diff --git a/core/startos/src/context/rpc.rs b/core/startos/src/context/rpc.rs index c78f118ea..6ada12301 100644 --- a/core/startos/src/context/rpc.rs +++ b/core/startos/src/context/rpc.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; use std::future::Future; use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use std::ops::Deref; @@ -9,8 +9,11 @@ use std::time::Duration; use chrono::{TimeDelta, Utc}; use helpers::NonDetachingJoinHandle; +use imbl::OrdMap; use imbl_value::InternedString; +use itertools::Itertools; use josekit::jwk::Jwk; +use models::{ActionId, PackageId}; use reqwest::{Client, Proxy}; use rpc_toolkit::yajrc::RpcError; use rpc_toolkit::{CallRemote, Context, Empty}; @@ -23,7 +26,6 @@ use crate::account::AccountInfo; use crate::auth::Sessions; use crate::context::config::ServerConfig; use crate::db::model::Database; -use crate::dependencies::compute_dependency_config_errs; use crate::disk::OsPartitionInfo; use crate::init::check_time_is_synchronized; use crate::lxc::{ContainerId, LxcContainer, LxcManager}; @@ -33,6 +35,7 @@ use crate::net::wifi::WpaCli; use crate::prelude::*; use crate::progress::{FullProgressTracker, PhaseProgressTrackerHandle}; use crate::rpc_continuations::{Guid, OpenAuthedContinuations, RpcContinuations}; +use crate::service::action::update_requested_actions; use crate::service::effects::callbacks::ServiceCallbacks; use crate::service::ServiceMap; use crate::shutdown::Shutdown; @@ -100,14 +103,14 @@ impl InitRpcContextPhases { pub struct CleanupInitPhases { cleanup_sessions: PhaseProgressTrackerHandle, init_services: PhaseProgressTrackerHandle, - check_dependencies: PhaseProgressTrackerHandle, + check_requested_actions: PhaseProgressTrackerHandle, } impl CleanupInitPhases { pub fn new(handle: &FullProgressTracker) -> Self { Self { cleanup_sessions: handle.add_phase("Cleaning up sessions".into(), Some(1)), init_services: handle.add_phase("Initializing services".into(), Some(10)), - check_dependencies: handle.add_phase("Checking dependencies".into(), Some(1)), + check_requested_actions: handle.add_phase("Checking action requests".into(), Some(1)), } } } @@ -309,7 +312,7 @@ impl RpcContext { CleanupInitPhases { mut cleanup_sessions, init_services, - mut check_dependencies, + mut check_requested_actions, }: CleanupInitPhases, ) -> Result<(), Error> { cleanup_sessions.start(); @@ -366,35 +369,68 @@ impl RpcContext { cleanup_sessions.complete(); self.services.init(&self, init_services).await?; - tracing::info!("Initialized Package Managers"); + tracing::info!("Initialized Services"); - check_dependencies.start(); - let mut updated_current_dependents = BTreeMap::new(); + // TODO + check_requested_actions.start(); let peek = self.db.peek().await; - for (package_id, package) in peek.as_public().as_package_data().as_entries()?.into_iter() { - let package = package.clone(); - let mut current_dependencies = package.as_current_dependencies().de()?; - compute_dependency_config_errs(self, &package_id, &mut current_dependencies) - .await - .log_err(); - updated_current_dependents.insert(package_id.clone(), current_dependencies); + let mut action_input: OrdMap> = OrdMap::new(); + let requested_actions: BTreeSet<_> = peek + .as_public() + .as_package_data() + .as_entries()? + .into_iter() + .map(|(_, pde)| { + Ok(pde + .as_requested_actions() + .as_entries()? + .into_iter() + .map(|(_, r)| { + Ok::<_, Error>(( + r.as_request().as_package_id().de()?, + r.as_request().as_action_id().de()?, + )) + })) + }) + .flatten_ok() + .map(|a| a.and_then(|a| a)) + .try_collect()?; + let procedure_id = Guid::new(); + for (package_id, action_id) in requested_actions { + if let Some(service) = self.services.get(&package_id).await.as_ref() { + if let Some(input) = service + .get_action_input(procedure_id.clone(), action_id.clone()) + .await? + .and_then(|i| i.value) + { + action_input + .entry(package_id) + .or_default() + .insert(action_id, input); + } + } } self.db - .mutate(|v| { - for (package_id, deps) in updated_current_dependents { - if let Some(model) = v - .as_public_mut() - .as_package_data_mut() - .as_idx_mut(&package_id) - .map(|i| i.as_current_dependencies_mut()) - { - model.ser(&deps)?; + .mutate(|db| { + for (package_id, action_input) in &action_input { + for (action_id, input) in action_input { + for (_, pde) in db.as_public_mut().as_package_data_mut().as_entries_mut()? { + pde.as_requested_actions_mut().mutate(|requested_actions| { + Ok(update_requested_actions( + requested_actions, + package_id, + action_id, + input, + false, + )) + })?; + } } } Ok(()) }) .await?; - check_dependencies.complete(); + check_requested_actions.complete(); Ok(()) } diff --git a/core/startos/src/db/model/package.rs b/core/startos/src/db/model/package.rs index 957e42c54..5fb6b9655 100644 --- a/core/startos/src/db/model/package.rs +++ b/core/startos/src/db/model/package.rs @@ -4,7 +4,8 @@ use chrono::{DateTime, Utc}; use exver::VersionRange; use imbl_value::InternedString; use models::{ - ActionId, DataUrl, HealthCheckId, HostId, PackageId, ServiceInterfaceId, VersionString, + ActionId, DataUrl, HealthCheckId, HostId, PackageId, ReplayId, ServiceInterfaceId, + VersionString, }; use patch_db::json_ptr::JsonPointer; use patch_db::HasModel; @@ -17,8 +18,8 @@ use crate::net::service_interface::ServiceInterface; use crate::prelude::*; use crate::progress::FullProgress; use crate::s9pk::manifest::Manifest; -use crate::status::Status; -use crate::util::serde::Pem; +use crate::status::MainStatus; +use crate::util::serde::{is_partial_of, Pem}; #[derive(Debug, Default, Deserialize, Serialize, TS)] #[ts(export)] @@ -310,9 +311,9 @@ pub struct InstallingInfo { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] #[ts(export)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "kebab-case")] pub enum AllowedStatuses { - OnlyRunning, // onlyRunning + OnlyRunning, OnlyStopped, Any, } @@ -324,13 +325,28 @@ pub struct ActionMetadata { pub name: String, pub description: String, pub warning: Option, - #[ts(type = "any")] - pub input: Value, - pub disabled: bool, + #[serde(default)] + pub visibility: ActionVisibility, pub allowed_statuses: AllowedStatuses, + pub has_input: bool, pub group: Option, } +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] +#[ts(export)] +#[serde(rename_all = "kebab-case")] +#[serde(rename_all_fields = "camelCase")] +pub enum ActionVisibility { + Hidden, + Disabled { reason: String }, + Enabled, +} +impl Default for ActionVisibility { + fn default() -> Self { + Self::Enabled + } +} + #[derive(Debug, Deserialize, Serialize, HasModel, TS)] #[serde(rename_all = "camelCase")] #[model = "Model"] @@ -338,7 +354,7 @@ pub struct ActionMetadata { pub struct PackageDataEntry { pub state_info: PackageState, pub data_version: Option, - pub status: Status, + pub status: MainStatus, #[ts(type = "string | null")] pub registry: Option, #[ts(type = "string")] @@ -348,6 +364,8 @@ pub struct PackageDataEntry { pub last_backup: Option>, pub current_dependencies: CurrentDependencies, pub actions: BTreeMap, + #[ts(as = "BTreeMap::")] + pub requested_actions: BTreeMap, pub service_interfaces: BTreeMap, pub hosts: Hosts, #[ts(type = "string[]")] @@ -384,8 +402,9 @@ impl Map for CurrentDependencies { } } -#[derive(Clone, Debug, Deserialize, Serialize, TS)] +#[derive(Clone, Debug, Deserialize, Serialize, TS, HasModel)] #[serde(rename_all = "camelCase")] +#[model = "Model"] pub struct CurrentDependencyInfo { #[ts(type = "string | null")] pub title: Option, @@ -394,11 +413,10 @@ pub struct CurrentDependencyInfo { pub kind: CurrentDependencyKind, #[ts(type = "string")] pub version_range: VersionRange, - pub config_satisfied: bool, } #[derive(Clone, Debug, Deserialize, Serialize, TS)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "kebab-case")] #[serde(tag = "kind")] pub enum CurrentDependencyKind { Exists, @@ -410,6 +428,66 @@ pub enum CurrentDependencyKind { }, } +#[derive(Clone, Debug, Deserialize, Serialize, TS, HasModel)] +#[serde(rename_all = "camelCase")] +#[ts(export)] +#[model = "Model"] +pub struct ActionRequestEntry { + pub request: ActionRequest, + pub active: bool, +} + +#[derive(Clone, Debug, Deserialize, Serialize, TS, HasModel)] +#[serde(rename_all = "camelCase")] +#[ts(export)] +#[model = "Model"] +pub struct ActionRequest { + pub package_id: PackageId, + pub action_id: ActionId, + #[ts(optional)] + pub description: Option, + #[ts(optional)] + pub when: Option, + #[ts(optional)] + pub input: Option, +} + +#[derive(Clone, Debug, Deserialize, Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct ActionRequestTrigger { + #[serde(default)] + pub once: bool, + pub condition: ActionRequestCondition, +} + +#[derive(Clone, Debug, Deserialize, Serialize, TS)] +#[serde(rename_all = "kebab-case")] +#[ts(export)] +pub enum ActionRequestCondition { + InputNotMatches, +} + +#[derive(Clone, Debug, Deserialize, Serialize, TS)] +#[serde(rename_all = "kebab-case")] +#[serde(tag = "kind")] +pub enum ActionRequestInput { + Partial { + #[ts(type = "Record")] + value: Value, + }, +} +impl ActionRequestInput { + pub fn matches(&self, input: Option<&Value>) -> bool { + match self { + Self::Partial { value } => match input { + None => false, + Some(full) => is_partial_of(value, full), + }, + } + } +} + #[derive(Debug, Default, Deserialize, Serialize)] pub struct InterfaceAddressMap(pub BTreeMap); impl Map for InterfaceAddressMap { diff --git a/core/startos/src/dependencies.rs b/core/startos/src/dependencies.rs index 013648980..3b55c8fc3 100644 --- a/core/startos/src/dependencies.rs +++ b/core/startos/src/dependencies.rs @@ -1,28 +1,14 @@ use std::collections::BTreeMap; -use std::time::Duration; -use clap::Parser; use imbl_value::InternedString; use models::PackageId; -use patch_db::json_patch::merge; -use rpc_toolkit::{from_fn_async, Context, Empty, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; -use tracing::instrument; use ts_rs::TS; -use crate::config::{Config, ConfigSpec, ConfigureContext}; -use crate::context::{CliContext, RpcContext}; -use crate::db::model::package::CurrentDependencies; use crate::prelude::*; -use crate::rpc_continuations::Guid; -use crate::util::serde::HandlerExtSerde; use crate::util::PathOrUrl; use crate::Error; -pub fn dependency() -> ParentHandler { - ParentHandler::new().subcommand("configure", configure::()) -} - #[derive(Clone, Debug, Default, Deserialize, Serialize, HasModel, TS)] #[model = "Model"] #[ts(export)] @@ -56,129 +42,3 @@ pub struct DependencyMetadata { #[ts(type = "string")] pub title: InternedString, } - -#[derive(Deserialize, Serialize, Parser, TS)] -#[serde(rename_all = "camelCase")] -#[command(rename_all = "kebab-case")] -pub struct ConfigureParams { - dependent_id: PackageId, - dependency_id: PackageId, -} -pub fn configure() -> ParentHandler { - ParentHandler::new() - .root_handler( - from_fn_async(configure_impl) - .with_inherited(|params, _| params) - .no_display() - .with_call_remote::(), - ) - .subcommand( - "dry", - from_fn_async(configure_dry) - .with_inherited(|params, _| params) - .with_display_serializable() - .with_call_remote::(), - ) -} - -pub async fn configure_impl( - ctx: RpcContext, - _: Empty, - ConfigureParams { - dependent_id, - dependency_id, - }: ConfigureParams, -) -> Result<(), Error> { - let ConfigDryRes { - old_config: _, - new_config, - spec: _, - } = configure_logic(ctx.clone(), (dependent_id, dependency_id.clone())).await?; - - let configure_context = ConfigureContext { - timeout: Some(Duration::from_secs(3).into()), - config: Some(new_config), - }; - ctx.services - .get(&dependency_id) - .await - .as_ref() - .ok_or_else(|| { - Error::new( - eyre!("There is no manager running for {dependency_id}"), - ErrorKind::Unknown, - ) - })? - .configure(Guid::new(), configure_context) - .await?; - Ok(()) -} - -pub async fn configure_dry( - ctx: RpcContext, - _: Empty, - ConfigureParams { - dependent_id, - dependency_id, - }: ConfigureParams, -) -> Result { - configure_logic(ctx.clone(), (dependent_id, dependency_id.clone())).await -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct ConfigDryRes { - pub old_config: Config, - pub new_config: Config, - pub spec: ConfigSpec, -} - -pub async fn configure_logic( - ctx: RpcContext, - (dependent_id, dependency_id): (PackageId, PackageId), -) -> Result { - let procedure_id = Guid::new(); - let dependency_guard = ctx.services.get(&dependency_id).await; - let dependency = dependency_guard.as_ref().or_not_found(&dependency_id)?; - let dependent_guard = ctx.services.get(&dependent_id).await; - let dependent = dependent_guard.as_ref().or_not_found(&dependent_id)?; - let config_res = dependency.get_config(procedure_id.clone()).await?; - let diff = Value::Object( - dependent - .dependency_config(procedure_id, dependency_id, config_res.config.clone()) - .await? - .unwrap_or_default(), - ); - let mut new_config = Value::Object(config_res.config.clone().unwrap_or_default()); - merge(&mut new_config, &diff); - Ok(ConfigDryRes { - old_config: config_res.config.unwrap_or_default(), - new_config: new_config.as_object().cloned().unwrap_or_default(), - spec: config_res.spec, - }) -} - -#[instrument(skip_all)] -pub async fn compute_dependency_config_errs( - ctx: &RpcContext, - id: &PackageId, - current_dependencies: &mut CurrentDependencies, -) -> Result<(), Error> { - let procedure_id = Guid::new(); - let service_guard = ctx.services.get(id).await; - let service = service_guard.as_ref().or_not_found(id)?; - for (dep_id, dep_info) in current_dependencies.0.iter_mut() { - // check if config passes dependency check - let Some(dependency) = &*ctx.services.get(dep_id).await else { - continue; - }; - - let dep_config = dependency.get_config(procedure_id.clone()).await?.config; - - dep_info.config_satisfied = service - .dependency_config(procedure_id.clone(), dep_id.clone(), dep_config) - .await? - .is_none(); - } - Ok(()) -} diff --git a/core/startos/src/disk/mount/backup.rs b/core/startos/src/disk/mount/backup.rs index 8f45b0d4f..2c322c284 100644 --- a/core/startos/src/disk/mount/backup.rs +++ b/core/startos/src/disk/mount/backup.rs @@ -219,10 +219,10 @@ impl Drop for BackupMountGuard { let second = self.backup_disk_mount_guard.take(); tokio::spawn(async move { if let Some(guard) = first { - guard.unmount().await.unwrap(); + guard.unmount().await.log_err(); } if let Some(guard) = second { - guard.unmount().await.unwrap(); + guard.unmount().await.log_err(); } }); } diff --git a/core/startos/src/disk/mount/filesystem/overlayfs.rs b/core/startos/src/disk/mount/filesystem/overlayfs.rs index 5e40a21a1..e8d1f0b34 100644 --- a/core/startos/src/disk/mount/filesystem/overlayfs.rs +++ b/core/startos/src/disk/mount/filesystem/overlayfs.rs @@ -151,12 +151,12 @@ impl Drop for OverlayGuard { let guard = self.inner_guard.take(); if lower.is_some() || upper.is_some() || guard.mounted { tokio::spawn(async move { - guard.unmount(false).await.unwrap(); + guard.unmount(false).await.log_err(); if let Some(lower) = lower { - lower.unmount().await.unwrap(); + lower.unmount().await.log_err(); } if let Some(upper) = upper { - upper.delete().await.unwrap(); + upper.delete().await.log_err(); } }); } diff --git a/core/startos/src/disk/mount/guard.rs b/core/startos/src/disk/mount/guard.rs index a2d577226..6e1cdc35e 100644 --- a/core/startos/src/disk/mount/guard.rs +++ b/core/startos/src/disk/mount/guard.rs @@ -96,7 +96,7 @@ impl Drop for MountGuard { fn drop(&mut self) { if self.mounted { let mountpoint = std::mem::take(&mut self.mountpoint); - tokio::spawn(async move { unmount(mountpoint, true).await.unwrap() }); + tokio::spawn(async move { unmount(mountpoint, true).await.log_err() }); } } } diff --git a/core/startos/src/install/mod.rs b/core/startos/src/install/mod.rs index 32b0f6ce4..f153c88ad 100644 --- a/core/startos/src/install/mod.rs +++ b/core/startos/src/install/mod.rs @@ -9,7 +9,7 @@ use exver::VersionRange; use futures::{AsyncWriteExt, StreamExt}; use imbl_value::{json, InternedString}; use itertools::Itertools; -use models::VersionString; +use models::{FromStrParser, VersionString}; use reqwest::header::{HeaderMap, CONTENT_LENGTH}; use reqwest::Url; use rpc_toolkit::yajrc::{GenericRpcMethod, RpcError}; @@ -30,7 +30,6 @@ use crate::registry::package::get::GetPackageResponse; use crate::rpc_continuations::{Guid, RpcContinuation}; use crate::s9pk::manifest::PackageId; use crate::upload::upload; -use crate::util::clap::FromStrParser; use crate::util::io::open_file; use crate::util::net::WebSocketExt; use crate::util::Never; diff --git a/core/startos/src/lib.rs b/core/startos/src/lib.rs index b5629aa04..8d4f831bf 100644 --- a/core/startos/src/lib.rs +++ b/core/startos/src/lib.rs @@ -29,7 +29,6 @@ pub mod action; pub mod auth; pub mod backup; pub mod bins; -pub mod config; pub mod context; pub mod control; pub mod db; @@ -70,7 +69,6 @@ pub mod volume; use std::time::SystemTime; use clap::Parser; -pub use config::Config; pub use error::{Error, ErrorKind, ResultExt}; use imbl_value::Value; use rpc_toolkit::yajrc::RpcError; @@ -240,15 +238,7 @@ pub fn server() -> ParentHandler { pub fn package() -> ParentHandler { ParentHandler::new() - .subcommand( - "action", - from_fn_async(action::action) - .with_display_serializable() - .with_custom_display_fn(|handle, result| { - Ok(action::display_action_result(handle.params, result)) - }) - .with_call_remote::(), - ) + .subcommand("action", action::action_api::()) .subcommand( "install", from_fn_async(install::install) @@ -281,7 +271,6 @@ pub fn package() -> ParentHandler { .with_display_serializable() .with_call_remote::(), ) - .subcommand("config", config::config::()) .subcommand( "start", from_fn_async(control::start) @@ -316,7 +305,6 @@ pub fn package() -> ParentHandler { }) .with_call_remote::(), ) - .subcommand("dependency", dependencies::dependency::()) .subcommand("backup", backup::package_backup::()) .subcommand("connect", from_fn_async(service::connect_rpc).no_cli()) .subcommand( diff --git a/core/startos/src/logs.rs b/core/startos/src/logs.rs index 9cf234f5f..7ff1d7d08 100644 --- a/core/startos/src/logs.rs +++ b/core/startos/src/logs.rs @@ -12,7 +12,7 @@ use color_eyre::eyre::eyre; use futures::stream::BoxStream; use futures::{Future, FutureExt, Stream, StreamExt, TryStreamExt}; use itertools::Itertools; -use models::PackageId; +use models::{FromStrParser, PackageId}; use rpc_toolkit::yajrc::RpcError; use rpc_toolkit::{ from_fn_async, CallRemote, Context, Empty, HandlerArgs, HandlerExt, HandlerFor, ParentHandler, @@ -30,7 +30,6 @@ use crate::error::ResultExt; use crate::lxc::ContainerId; use crate::prelude::*; use crate::rpc_continuations::{Guid, RpcContinuation, RpcContinuations}; -use crate::util::clap::FromStrParser; use crate::util::serde::Reversible; use crate::util::Invoke; @@ -487,14 +486,18 @@ fn logs_follow< context, inherited_params: LogsParams { - extra, limit, boot, .. + extra, + cursor, + limit, + boot, + .. }, .. }: HandlerArgs>| { let f = f.clone(); async move { let src = f.call(&context, extra).await?; - follow_logs(context, src, limit, boot.map(String::from)).await + follow_logs(context, src, cursor, limit, boot.map(String::from)).await } }, ) @@ -525,7 +528,7 @@ pub fn package_logs() -> ParentHandler> pub async fn journalctl( id: LogSource, - limit: usize, + limit: Option, cursor: Option<&str>, boot: Option<&str>, before: bool, @@ -533,11 +536,12 @@ pub async fn journalctl( ) -> Result { let mut cmd = gen_journalctl_command(&id); - cmd.arg(format!("--lines={}", limit)); + if let Some(limit) = limit { + cmd.arg(format!("--lines={}", limit)); + } - let cursor_formatted = format!("--after-cursor={}", cursor.unwrap_or("")); - if cursor.is_some() { - cmd.arg(&cursor_formatted); + if let Some(cursor) = cursor { + cmd.arg(&format!("--after-cursor={}", cursor)); if before { cmd.arg("--reverse"); } @@ -638,8 +642,15 @@ pub async fn fetch_logs( before: bool, ) -> Result { let limit = limit.unwrap_or(50); - let mut stream = - journalctl(id, limit, cursor.as_deref(), boot.as_deref(), before, false).await?; + let mut stream = journalctl( + id, + Some(limit), + cursor.as_deref(), + boot.as_deref(), + before, + false, + ) + .await?; let mut entries = Vec::with_capacity(limit); let mut start_cursor = None; @@ -682,11 +693,16 @@ pub async fn fetch_logs( pub async fn follow_logs>( ctx: Context, id: LogSource, + cursor: Option, limit: Option, boot: Option, ) -> Result { - let limit = limit.unwrap_or(50); - let mut stream = journalctl(id, limit, None, boot.as_deref(), false, true).await?; + let limit = if cursor.is_some() { + None + } else { + Some(limit.unwrap_or(50)) + }; + let mut stream = journalctl(id, limit, cursor.as_deref(), boot.as_deref(), false, true).await?; let mut start_cursor = None; let mut first_entry = None; diff --git a/core/startos/src/lxc/mod.rs b/core/startos/src/lxc/mod.rs index 480f7a24c..ee08801df 100644 --- a/core/startos/src/lxc/mod.rs +++ b/core/startos/src/lxc/mod.rs @@ -7,7 +7,7 @@ use std::time::Duration; use clap::builder::ValueParserFactory; use futures::{AsyncWriteExt, StreamExt}; use imbl_value::{InOMap, InternedString}; -use models::InvalidId; +use models::{FromStrParser, InvalidId}; use rpc_toolkit::yajrc::RpcError; use rpc_toolkit::{GenericRpcMethod, RpcRequest, RpcResponse}; use rustyline_async::{ReadlineEvent, SharedWriter}; @@ -28,7 +28,6 @@ use crate::disk::mount::guard::{GenericMountGuard, MountGuard, TmpMountGuard}; use crate::disk::mount::util::unmount; use crate::prelude::*; use crate::rpc_continuations::{Guid, RpcContinuation}; -use crate::util::clap::FromStrParser; use crate::util::io::open_file; use crate::util::rpc_client::UnixRpcClient; use crate::util::{new_guid, Invoke}; @@ -365,7 +364,7 @@ impl Drop for LxcContainer { tracing::error!("Error reading logs from crashed container: {e}"); tracing::debug!("{e:?}") } - rootfs.unmount(true).await.unwrap(); + rootfs.unmount(true).await.log_err(); drop(guid); if let Err(e) = manager.gc().await { tracing::error!("Error cleaning up dangling LXC containers: {e}"); diff --git a/core/startos/src/net/host/binding.rs b/core/startos/src/net/host/binding.rs index 76dd04059..174f0330f 100644 --- a/core/startos/src/net/host/binding.rs +++ b/core/startos/src/net/host/binding.rs @@ -1,3 +1,7 @@ +use std::str::FromStr; + +use clap::builder::ValueParserFactory; +use models::{FromStrParser, HostId}; use serde::{Deserialize, Serialize}; use ts_rs::TS; @@ -5,10 +9,37 @@ use crate::net::forward::AvailablePorts; use crate::net::vhost::AlpnInfo; use crate::prelude::*; +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, TS)] +#[ts(export)] +#[serde(rename_all = "camelCase")] +pub struct BindId { + pub id: HostId, + pub internal_port: u16, +} +impl ValueParserFactory for BindId { + type Parser = FromStrParser; + fn value_parser() -> Self::Parser { + FromStrParser::new() + } +} +impl FromStr for BindId { + type Err = Error; + fn from_str(s: &str) -> Result { + let (id, port) = s + .split_once(":") + .ok_or_else(|| Error::new(eyre!("expected :"), ErrorKind::ParseUrl))?; + Ok(Self { + id: id.parse()?, + internal_port: port.parse()?, + }) + } +} + #[derive(Debug, Deserialize, Serialize, TS)] #[serde(rename_all = "camelCase")] #[ts(export)] pub struct BindInfo { + pub enabled: bool, pub options: BindOptions, pub lan: LanInfo, } @@ -30,6 +61,7 @@ impl BindInfo { assigned_ssl_port = Some(available_ports.alloc()?); } Ok(Self { + enabled: true, options, lan: LanInfo { assigned_port, @@ -69,7 +101,14 @@ impl BindInfo { available_ports.free([port]); } } - Ok(Self { options, lan }) + Ok(Self { + enabled: true, + options, + lan, + }) + } + pub fn disable(&mut self) { + self.enabled = false; } } diff --git a/core/startos/src/net/net_controller.rs b/core/startos/src/net/net_controller.rs index b7a8022b4..a94c7a7d0 100644 --- a/core/startos/src/net/net_controller.rs +++ b/core/startos/src/net/net_controller.rs @@ -15,8 +15,8 @@ use crate::hostname::Hostname; use crate::net::dns::DnsController; use crate::net::forward::LanPortForwardController; use crate::net::host::address::HostAddress; -use crate::net::host::binding::{AddSslOptions, BindOptions, LanInfo}; -use crate::net::host::{host_for, Host, HostKind}; +use crate::net::host::binding::{AddSslOptions, BindId, BindOptions, LanInfo}; +use crate::net::host::{host_for, Host, HostKind, Hosts}; use crate::net::service_interface::{HostnameInfo, IpHostname, OnionHostname}; use crate::net::tor::TorController; use crate::net::vhost::{AlpnInfo, VHostController}; @@ -154,14 +154,16 @@ impl NetController { ) -> Result { let dns = self.dns.add(Some(package.clone()), ip).await?; - Ok(NetService { + let mut res = NetService { shutdown: false, id: package, ip, dns, controller: Arc::downgrade(self), binds: BTreeMap::new(), - }) + }; + res.clear_bindings(Default::default()).await?; + Ok(res) } } @@ -221,31 +223,41 @@ impl NetService { self.update(id, host).await } - pub async fn clear_bindings(&mut self) -> Result<(), Error> { - let ctrl = self.net_controller()?; + pub async fn clear_bindings(&mut self, except: BTreeSet) -> Result<(), Error> { + let pkg_id = &self.id; + let hosts = self + .net_controller()? + .db + .mutate(|db| { + let mut res = Hosts::default(); + for (host_id, host) in db + .as_public_mut() + .as_package_data_mut() + .as_idx_mut(pkg_id) + .or_not_found(pkg_id)? + .as_hosts_mut() + .as_entries_mut()? + { + host.as_bindings_mut().mutate(|b| { + for (internal_port, info) in b { + if !except.contains(&BindId { + id: host_id.clone(), + internal_port: *internal_port, + }) { + info.disable(); + } + } + Ok(()) + })?; + res.0.insert(host_id, host.de()?); + } + Ok(res) + }) + .await?; let mut errors = ErrorCollection::new(); - for (_, binds) in std::mem::take(&mut self.binds) { - for (_, (lan, _, hostnames, rc)) in binds.lan { - drop(rc); - if let Some(external) = lan.assigned_ssl_port { - for hostname in ctrl.server_hostnames.iter().cloned() { - ctrl.vhost.gc(hostname, external).await?; - } - for hostname in hostnames { - ctrl.vhost.gc(Some(hostname), external).await?; - } - } - if let Some(external) = lan.assigned_port { - ctrl.forward.gc(external).await?; - } - } - for (addr, (_, rcs)) in binds.tor { - drop(rcs); - errors.handle(ctrl.tor.gc(Some(addr), None).await); - } + for (id, host) in hosts.0 { + errors.handle(self.update(id, host).await); } - std::mem::take(&mut self.dns); - errors.handle(ctrl.dns.gc(Some(self.id.clone()), self.ip).await); errors.into_result() } @@ -261,6 +273,9 @@ impl NetService { let ip_info = server_info.as_ip_info().de()?; let hostname = server_info.as_hostname().de()?; for (port, bind) in &host.bindings { + if !bind.enabled { + continue; + } let old_lan_bind = binds.lan.remove(port); let lan_bind = old_lan_bind .as_ref() @@ -395,7 +410,7 @@ impl NetService { } let mut removed = BTreeSet::new(); binds.lan.retain(|internal, (external, _, hostnames, _)| { - if host.bindings.contains_key(internal) { + if host.bindings.get(internal).map_or(false, |b| b.enabled) { true } else { removed.insert((*external, std::mem::take(hostnames))); @@ -424,6 +439,9 @@ impl NetService { let mut tor_hostname_ports = BTreeMap::::new(); let mut tor_binds = OrdMap::::new(); for (internal, info) in &host.bindings { + if !info.enabled { + continue; + } tor_binds.insert( info.options.preferred_external_port, SocketAddr::from((self.ip, *internal)), @@ -511,7 +529,7 @@ impl NetService { pub async fn remove_all(mut self) -> Result<(), Error> { self.shutdown = true; if let Some(ctrl) = Weak::upgrade(&self.controller) { - self.clear_bindings().await?; + self.clear_bindings(Default::default()).await?; drop(ctrl); Ok(()) } else { @@ -566,7 +584,7 @@ impl Drop for NetService { binds: BTreeMap::new(), }, ); - tokio::spawn(async move { svc.remove_all().await.unwrap() }); + tokio::spawn(async move { svc.remove_all().await.log_err() }); } } } diff --git a/core/startos/src/net/tor.rs b/core/startos/src/net/tor.rs index 24b8ddb02..16705776f 100644 --- a/core/startos/src/net/tor.rs +++ b/core/startos/src/net/tor.rs @@ -307,7 +307,7 @@ async fn torctl( let logs = journalctl( LogSource::Unit(SYSTEMD_UNIT), - 0, + Some(0), None, Some("0"), false, diff --git a/core/startos/src/notifications.rs b/core/startos/src/notifications.rs index c99ffb356..c8e1725f4 100644 --- a/core/startos/src/notifications.rs +++ b/core/startos/src/notifications.rs @@ -7,7 +7,7 @@ use clap::builder::ValueParserFactory; use clap::Parser; use color_eyre::eyre::eyre; use imbl_value::InternedString; -use models::PackageId; +use models::{FromStrParser, PackageId}; use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; use tracing::instrument; @@ -17,7 +17,6 @@ use crate::backup::BackupReport; use crate::context::{CliContext, RpcContext}; use crate::db::model::DatabaseModel; use crate::prelude::*; -use crate::util::clap::FromStrParser; use crate::util::serde::HandlerExtSerde; // #[command(subcommands(list, delete, delete_before, create))] diff --git a/core/startos/src/registry/signer/mod.rs b/core/startos/src/registry/signer/mod.rs index 99b23b88e..c203f0dad 100644 --- a/core/startos/src/registry/signer/mod.rs +++ b/core/startos/src/registry/signer/mod.rs @@ -3,6 +3,7 @@ use std::str::FromStr; use clap::builder::ValueParserFactory; use itertools::Itertools; +use models::FromStrParser; use serde::{Deserialize, Serialize}; use ts_rs::TS; use url::Url; @@ -10,7 +11,6 @@ use url::Url; use crate::prelude::*; use crate::registry::signer::commitment::Digestable; use crate::registry::signer::sign::{AnySignature, AnyVerifyingKey, SignatureScheme}; -use crate::util::clap::FromStrParser; pub mod commitment; pub mod sign; diff --git a/core/startos/src/registry/signer/sign/mod.rs b/core/startos/src/registry/signer/sign/mod.rs index a29109864..6a95a2490 100644 --- a/core/startos/src/registry/signer/sign/mod.rs +++ b/core/startos/src/registry/signer/sign/mod.rs @@ -4,6 +4,7 @@ use std::str::FromStr; use ::ed25519::pkcs8::BitStringRef; use clap::builder::ValueParserFactory; use der::referenced::OwnedToRef; +use models::FromStrParser; use pkcs8::der::AnyRef; use pkcs8::{PrivateKeyInfo, SubjectPublicKeyInfo}; use serde::{Deserialize, Serialize}; @@ -13,7 +14,6 @@ use ts_rs::TS; use crate::prelude::*; use crate::registry::signer::commitment::Digestable; use crate::registry::signer::sign::ed25519::Ed25519; -use crate::util::clap::FromStrParser; use crate::util::serde::{deserialize_from_str, serialize_display}; pub mod ed25519; diff --git a/core/startos/src/rpc_continuations.rs b/core/startos/src/rpc_continuations.rs index 043130b69..4614f8fa6 100644 --- a/core/startos/src/rpc_continuations.rs +++ b/core/startos/src/rpc_continuations.rs @@ -13,12 +13,12 @@ use futures::future::BoxFuture; use futures::{Future, FutureExt}; use helpers::TimedResource; use imbl_value::InternedString; +use models::FromStrParser; use tokio::sync::{broadcast, Mutex as AsyncMutex}; use ts_rs::TS; #[allow(unused_imports)] use crate::prelude::*; -use crate::util::clap::FromStrParser; use crate::util::new_guid; #[derive( diff --git a/core/startos/src/s9pk/v2/compat.rs b/core/startos/src/s9pk/v2/compat.rs index 8e62c69d0..e3f54d3a2 100644 --- a/core/startos/src/s9pk/v2/compat.rs +++ b/core/startos/src/s9pk/v2/compat.rs @@ -249,7 +249,6 @@ impl TryFrom for Manifest { hardware_requirements: value.hardware_requirements, git_hash: value.git_hash, os_version: value.eos_version, - has_config: value.config.is_some(), }) } } diff --git a/core/startos/src/s9pk/v2/manifest.rs b/core/startos/src/s9pk/v2/manifest.rs index 1f24a0b73..f8102c736 100644 --- a/core/startos/src/s9pk/v2/manifest.rs +++ b/core/startos/src/s9pk/v2/manifest.rs @@ -68,8 +68,6 @@ pub struct Manifest { #[serde(default = "current_version")] #[ts(type = "string")] pub os_version: Version, - #[serde(default = "const_true")] - pub has_config: bool, } impl Manifest { pub fn validate_for<'a, T: Clone>( diff --git a/core/startos/src/service/action.rs b/core/startos/src/service/action.rs index 6c5ac4eab..4068d5ad8 100644 --- a/core/startos/src/service/action.rs +++ b/core/startos/src/service/action.rs @@ -1,42 +1,38 @@ +use std::collections::BTreeMap; use std::time::Duration; -use models::{ActionId, ProcedureName}; +use imbl_value::json; +use models::{ActionId, PackageId, ProcedureName, ReplayId}; -use crate::action::ActionResult; +use crate::action::{ActionInput, ActionResult}; +use crate::db::model::package::{ActionRequestCondition, ActionRequestEntry, ActionRequestInput}; use crate::prelude::*; use crate::rpc_continuations::Guid; -use crate::service::config::GetConfig; -use crate::service::dependencies::DependencyConfig; use crate::service::{Service, ServiceActor}; use crate::util::actor::background::BackgroundJobQueue; use crate::util::actor::{ConflictBuilder, Handler}; +use crate::util::serde::is_partial_of; -pub(super) struct Action { +pub(super) struct GetActionInput { id: ActionId, - input: Value, } -impl Handler for ServiceActor { - type Response = Result; - fn conflicts_with(_: &Action) -> ConflictBuilder { - ConflictBuilder::everything() - .except::() - .except::() +impl Handler for ServiceActor { + type Response = Result, Error>; + fn conflicts_with(_: &GetActionInput) -> ConflictBuilder { + ConflictBuilder::nothing() } async fn handle( &mut self, id: Guid, - Action { - id: action_id, - input, - }: Action, + GetActionInput { id: action_id }: GetActionInput, _: &BackgroundJobQueue, ) -> Self::Response { let container = &self.0.persistent_container; container - .execute::( + .execute::>( id, - ProcedureName::RunAction(action_id), - input, + ProcedureName::GetActionInput(action_id), + Value::Null, Some(Duration::from_secs(30)), ) .await @@ -45,16 +41,139 @@ impl Handler for ServiceActor { } impl Service { - pub async fn action( + pub async fn get_action_input( + &self, + id: Guid, + action_id: ActionId, + ) -> Result, Error> { + if !self + .seed + .ctx + .db + .peek() + .await + .as_public() + .as_package_data() + .as_idx(&self.seed.id) + .or_not_found(&self.seed.id)? + .as_actions() + .as_idx(&action_id) + .or_not_found(&action_id)? + .as_has_input() + .de()? + { + return Ok(None); + } + self.actor + .send(id, GetActionInput { id: action_id }) + .await? + } +} + +pub fn update_requested_actions( + requested_actions: &mut BTreeMap, + package_id: &PackageId, + action_id: &ActionId, + input: &Value, + was_run: bool, +) { + requested_actions.retain(|_, v| { + if &v.request.package_id != package_id || &v.request.action_id != action_id { + return true; + } + if let Some(when) = &v.request.when { + match &when.condition { + ActionRequestCondition::InputNotMatches => match &v.request.input { + Some(ActionRequestInput::Partial { value }) => { + if is_partial_of(value, input) { + if when.once { + return !was_run; + } else { + v.active = false; + } + } else { + v.active = true; + } + } + None => { + tracing::error!( + "action request exists in an invalid state {:?}", + v.request + ); + } + }, + } + true + } else { + !was_run + } + }) +} + +pub(super) struct RunAction { + id: ActionId, + input: Value, +} +impl Handler for ServiceActor { + type Response = Result, Error>; + fn conflicts_with(_: &RunAction) -> ConflictBuilder { + ConflictBuilder::everything().except::() + } + async fn handle( + &mut self, + id: Guid, + RunAction { + id: action_id, + input, + }: RunAction, + _: &BackgroundJobQueue, + ) -> Self::Response { + let container = &self.0.persistent_container; + let result = container + .execute::>( + id, + ProcedureName::RunAction(action_id.clone()), + json!({ + "input": input, + }), + Some(Duration::from_secs(30)), + ) + .await + .with_kind(ErrorKind::Action)?; + let package_id = &self.0.id; + self.0 + .ctx + .db + .mutate(|db| { + for (_, pde) in db.as_public_mut().as_package_data_mut().as_entries_mut()? { + pde.as_requested_actions_mut().mutate(|requested_actions| { + Ok(update_requested_actions( + requested_actions, + package_id, + &action_id, + &input, + true, + )) + })?; + } + Ok(()) + }) + .await?; + Ok(result) + } +} + +impl Service { + pub async fn run_action( &self, id: Guid, action_id: ActionId, input: Value, - ) -> Result { + ) -> Result, Error> { self.actor .send( id, - Action { + RunAction { id: action_id, input, }, diff --git a/core/startos/src/service/config.rs b/core/startos/src/service/config.rs deleted file mode 100644 index faa70fc41..000000000 --- a/core/startos/src/service/config.rs +++ /dev/null @@ -1,78 +0,0 @@ -use std::time::Duration; - -use models::ProcedureName; - -use crate::config::action::ConfigRes; -use crate::config::ConfigureContext; -use crate::prelude::*; -use crate::rpc_continuations::Guid; -use crate::service::dependencies::DependencyConfig; -use crate::service::{Service, ServiceActor}; -use crate::util::actor::background::BackgroundJobQueue; -use crate::util::actor::{ConflictBuilder, Handler}; -use crate::util::serde::NoOutput; - -pub(super) struct Configure(ConfigureContext); -impl Handler for ServiceActor { - type Response = Result<(), Error>; - fn conflicts_with(_: &Configure) -> ConflictBuilder { - ConflictBuilder::everything().except::() - } - async fn handle( - &mut self, - id: Guid, - Configure(ConfigureContext { timeout, config }): Configure, - _: &BackgroundJobQueue, - ) -> Self::Response { - let container = &self.0.persistent_container; - let package_id = &self.0.id; - - container - .execute::(id, ProcedureName::SetConfig, to_value(&config)?, timeout) - .await - .with_kind(ErrorKind::ConfigRulesViolation)?; - self.0 - .ctx - .db - .mutate(move |db| { - db.as_public_mut() - .as_package_data_mut() - .as_idx_mut(package_id) - .or_not_found(package_id)? - .as_status_mut() - .as_configured_mut() - .ser(&true) - }) - .await?; - Ok(()) - } -} - -pub(super) struct GetConfig; -impl Handler for ServiceActor { - type Response = Result; - fn conflicts_with(_: &GetConfig) -> ConflictBuilder { - ConflictBuilder::nothing().except::() - } - async fn handle(&mut self, id: Guid, _: GetConfig, _: &BackgroundJobQueue) -> Self::Response { - let container = &self.0.persistent_container; - container - .execute::( - id, - ProcedureName::GetConfig, - Value::Null, - Some(Duration::from_secs(30)), // TODO timeout - ) - .await - .with_kind(ErrorKind::ConfigRulesViolation) - } -} - -impl Service { - pub async fn configure(&self, id: Guid, ctx: ConfigureContext) -> Result<(), Error> { - self.actor.send(id, Configure(ctx)).await? - } - pub async fn get_config(&self, id: Guid) -> Result { - self.actor.send(id, GetConfig).await? - } -} diff --git a/core/startos/src/service/control.rs b/core/startos/src/service/control.rs index 7c4bdf815..8b920bb32 100644 --- a/core/startos/src/service/control.rs +++ b/core/startos/src/service/control.rs @@ -1,7 +1,6 @@ use crate::prelude::*; use crate::rpc_continuations::Guid; -use crate::service::config::GetConfig; -use crate::service::dependencies::DependencyConfig; +use crate::service::action::RunAction; use crate::service::start_stop::StartStop; use crate::service::transition::TransitionKind; use crate::service::{Service, ServiceActor}; @@ -12,9 +11,7 @@ pub(super) struct Start; impl Handler for ServiceActor { type Response = (); fn conflicts_with(_: &Start) -> ConflictBuilder { - ConflictBuilder::everything() - .except::() - .except::() + ConflictBuilder::everything().except::() } async fn handle(&mut self, _: Guid, _: Start, _: &BackgroundJobQueue) -> Self::Response { self.0.persistent_container.state.send_modify(|x| { @@ -33,9 +30,7 @@ struct Stop; impl Handler for ServiceActor { type Response = (); fn conflicts_with(_: &Stop) -> ConflictBuilder { - ConflictBuilder::everything() - .except::() - .except::() + ConflictBuilder::everything().except::() } async fn handle(&mut self, _: Guid, _: Stop, _: &BackgroundJobQueue) -> Self::Response { let mut transition_state = None; diff --git a/core/startos/src/service/dependencies.rs b/core/startos/src/service/dependencies.rs deleted file mode 100644 index e8c6f07c4..000000000 --- a/core/startos/src/service/dependencies.rs +++ /dev/null @@ -1,86 +0,0 @@ -use std::time::Duration; - -use imbl_value::json; -use models::{PackageId, ProcedureName}; - -use crate::prelude::*; -use crate::rpc_continuations::Guid; -use crate::service::{Service, ServiceActor, ServiceActorSeed}; -use crate::util::actor::background::BackgroundJobQueue; -use crate::util::actor::{ConflictBuilder, Handler}; -use crate::Config; - -impl ServiceActorSeed { - async fn dependency_config( - &self, - id: Guid, - dependency_id: PackageId, - remote_config: Option, - ) -> Result, Error> { - let container = &self.persistent_container; - container - .sanboxed::>( - id.clone(), - ProcedureName::UpdateDependency(dependency_id.clone()), - json!({ - "queryResults": container - .execute::( - id, - ProcedureName::QueryDependency(dependency_id), - Value::Null, - Some(Duration::from_secs(30)), - ) - .await - .with_kind(ErrorKind::Dependency)?, - "remoteConfig": remote_config, - }), - Some(Duration::from_secs(30)), - ) - .await - .with_kind(ErrorKind::Dependency) - .map(|res| res.filter(|c| !c.is_empty() && Some(c) != remote_config.as_ref())) - } -} - -pub(super) struct DependencyConfig { - dependency_id: PackageId, - remote_config: Option, -} -impl Handler for ServiceActor { - type Response = Result, Error>; - fn conflicts_with(_: &DependencyConfig) -> ConflictBuilder { - ConflictBuilder::nothing() - } - async fn handle( - &mut self, - id: Guid, - DependencyConfig { - dependency_id, - remote_config, - }: DependencyConfig, - _: &BackgroundJobQueue, - ) -> Self::Response { - self.0 - .dependency_config(id, dependency_id, remote_config) - .await - } -} - -impl Service { - pub async fn dependency_config( - &self, - id: Guid, - dependency_id: PackageId, - remote_config: Option, - ) -> Result, Error> { - self.actor - .send( - id, - DependencyConfig { - dependency_id, - remote_config, - }, - ) - .await? - } -} diff --git a/core/startos/src/service/effects/action.rs b/core/startos/src/service/effects/action.rs index 4719c6d3d..5e3605679 100644 --- a/core/startos/src/service/effects/action.rs +++ b/core/startos/src/service/effects/action.rs @@ -1,22 +1,59 @@ -use std::collections::BTreeMap; +use std::collections::BTreeSet; -use models::{ActionId, PackageId}; +use models::{ActionId, PackageId, ReplayId}; +use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler}; -use crate::action::ActionResult; -use crate::db::model::package::ActionMetadata; +use crate::action::{display_action_result, ActionInput, ActionResult}; +use crate::db::model::package::{ + ActionMetadata, ActionRequest, ActionRequestCondition, ActionRequestEntry, ActionRequestTrigger, +}; use crate::rpc_continuations::Guid; +use crate::service::cli::ContainerCliContext; use crate::service::effects::prelude::*; +use crate::util::serde::HandlerExtSerde; + +pub fn action_api() -> ParentHandler { + ParentHandler::new() + .subcommand("export", from_fn_async(export_action).no_cli()) + .subcommand( + "clear", + from_fn_async(clear_actions) + .no_display() + .with_call_remote::(), + ) + .subcommand( + "get-input", + from_fn_async(get_action_input) + .with_display_serializable() + .with_call_remote::(), + ) + .subcommand( + "run", + from_fn_async(run_action) + .with_display_serializable() + .with_custom_display_fn(|args, res| Ok(display_action_result(args.params, res))) + .with_call_remote::(), + ) + .subcommand("request", from_fn_async(request_action).no_cli()) + .subcommand( + "clear-requests", + from_fn_async(clear_action_requests) + .no_display() + .with_call_remote::(), + ) +} #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[ts(export)] #[serde(rename_all = "camelCase")] pub struct ExportActionParams { - #[ts(optional)] - package_id: Option, id: ActionId, metadata: ActionMetadata, } -pub async fn export_action(context: EffectContext, data: ExportActionParams) -> Result<(), Error> { +pub async fn export_action( + context: EffectContext, + ExportActionParams { id, metadata }: ExportActionParams, +) -> Result<(), Error> { let context = context.deref()?; let package_id = context.seed.id.clone(); context @@ -31,17 +68,26 @@ pub async fn export_action(context: EffectContext, data: ExportActionParams) -> .or_not_found(&package_id)? .as_actions_mut(); let mut value = model.de()?; - value - .insert(data.id, data.metadata) - .map(|_| ()) - .unwrap_or_default(); + value.insert(id, metadata); model.ser(&value) }) .await?; Ok(()) } -pub async fn clear_actions(context: EffectContext) -> Result<(), Error> { +#[derive(Debug, Clone, Serialize, Deserialize, TS, Parser)] +#[ts(export)] +#[serde(rename_all = "camelCase")] +pub struct ClearActionsParams { + #[arg(long)] + pub except: Vec, +} + +async fn clear_actions( + context: EffectContext, + ClearActionsParams { except }: ClearActionsParams, +) -> Result<(), Error> { + let except: BTreeSet<_> = except.into_iter().collect(); let context = context.deref()?; let package_id = context.seed.id.clone(); context @@ -54,34 +100,32 @@ pub async fn clear_actions(context: EffectContext) -> Result<(), Error> { .as_idx_mut(&package_id) .or_not_found(&package_id)? .as_actions_mut() - .ser(&BTreeMap::new()) + .mutate(|a| Ok(a.retain(|e, _| except.contains(e)))) }) .await?; Ok(()) } -#[derive(Debug, Clone, Serialize, Deserialize, TS)] +#[derive(Debug, Clone, Serialize, Deserialize, TS, Parser)] #[serde(rename_all = "camelCase")] #[ts(export)] -pub struct ExecuteAction { +pub struct GetActionInputParams { #[serde(default)] #[ts(skip)] + #[arg(skip)] procedure_id: Guid, #[ts(optional)] package_id: Option, action_id: ActionId, - #[ts(type = "any")] - input: Value, } -pub async fn execute_action( +async fn get_action_input( context: EffectContext, - ExecuteAction { + GetActionInputParams { procedure_id, package_id, action_id, - input, - }: ExecuteAction, -) -> Result { + }: GetActionInputParams, +) -> Result, Error> { let context = context.deref()?; if let Some(package_id) = package_id { @@ -93,9 +137,179 @@ pub async fn execute_action( .await .as_ref() .or_not_found(&package_id)? - .action(procedure_id, action_id, input) + .get_action_input(procedure_id, action_id) .await } else { - context.action(procedure_id, action_id, input).await + context.get_action_input(procedure_id, action_id).await } } + +#[derive(Debug, Clone, Serialize, Deserialize, TS, Parser)] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct RunActionParams { + #[serde(default)] + #[ts(skip)] + #[arg(skip)] + procedure_id: Guid, + #[ts(optional)] + package_id: Option, + action_id: ActionId, + #[ts(type = "any")] + input: Value, +} +async fn run_action( + context: EffectContext, + RunActionParams { + procedure_id, + package_id, + action_id, + input, + }: RunActionParams, +) -> Result, Error> { + let context = context.deref()?; + + let package_id = package_id.as_ref().unwrap_or(&context.seed.id); + + if package_id != &context.seed.id { + return Err(Error::new( + eyre!("calling actions on other packages is unsupported at this time"), + ErrorKind::InvalidRequest, + )); + context + .seed + .ctx + .services + .get(&package_id) + .await + .as_ref() + .or_not_found(&package_id)? + .run_action(procedure_id, action_id, input) + .await + } else { + context.run_action(procedure_id, action_id, input).await + } +} + +#[derive(Clone, Debug, Deserialize, Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct RequestActionParams { + #[serde(default)] + #[ts(skip)] + procedure_id: Guid, + replay_id: ReplayId, + #[serde(flatten)] + request: ActionRequest, +} +async fn request_action( + context: EffectContext, + RequestActionParams { + procedure_id, + replay_id, + request, + }: RequestActionParams, +) -> Result<(), Error> { + let context = context.deref()?; + + let src_id = &context.seed.id; + let active = match &request.when { + Some(ActionRequestTrigger { once, condition }) => match condition { + ActionRequestCondition::InputNotMatches => { + let Some(input) = request.input.as_ref() else { + return Err(Error::new( + eyre!("input-not-matches trigger requires input to be specified"), + ErrorKind::InvalidRequest, + )); + }; + if let Some(service) = context + .seed + .ctx + .services + .get(&request.package_id) + .await + .as_ref() + { + let Some(prev) = service + .get_action_input(procedure_id, request.action_id.clone()) + .await? + else { + return Err(Error::new( + eyre!( + "action {} of {} has no input", + request.action_id, + request.package_id + ), + ErrorKind::InvalidRequest, + )); + }; + if input.matches(prev.value.as_ref()) { + if *once { + return Ok(()); + } else { + false + } + } else { + true + } + } else { + true // update when service is installed + } + } + }, + None => true, + }; + context + .seed + .ctx + .db + .mutate(|db| { + db.as_public_mut() + .as_package_data_mut() + .as_idx_mut(src_id) + .or_not_found(src_id)? + .as_requested_actions_mut() + .insert(&replay_id, &ActionRequestEntry { active, request }) + }) + .await?; + Ok(()) +} + +#[derive(Debug, Clone, Serialize, Deserialize, TS, Parser)] +#[ts(type = "{ only: string[] } | { except: string[] }")] +#[ts(export)] +pub struct ClearActionRequestsParams { + #[arg(long, conflicts_with = "except")] + pub only: Option>, + #[arg(long, conflicts_with = "only")] + pub except: Option>, +} + +async fn clear_action_requests( + context: EffectContext, + ClearActionRequestsParams { only, except }: ClearActionRequestsParams, +) -> Result<(), Error> { + let context = context.deref()?; + let package_id = context.seed.id.clone(); + let only = only.map(|only| only.into_iter().collect::>()); + let except = except.map(|except| except.into_iter().collect::>()); + context + .seed + .ctx + .db + .mutate(|db| { + db.as_public_mut() + .as_package_data_mut() + .as_idx_mut(&package_id) + .or_not_found(&package_id)? + .as_requested_actions_mut() + .mutate(|a| { + Ok(a.retain(|e, _| { + only.as_ref().map_or(true, |only| !only.contains(e)) + && except.as_ref().map_or(true, |except| except.contains(e)) + })) + }) + }) + .await?; + Ok(()) +} diff --git a/core/startos/src/service/effects/callbacks.rs b/core/startos/src/service/effects/callbacks.rs index 1a9250aa8..05f36cb4b 100644 --- a/core/startos/src/service/effects/callbacks.rs +++ b/core/startos/src/service/effects/callbacks.rs @@ -3,19 +3,22 @@ use std::collections::{BTreeMap, BTreeSet}; use std::sync::{Arc, Mutex, Weak}; use std::time::{Duration, SystemTime}; +use clap::Parser; use futures::future::join_all; use helpers::NonDetachingJoinHandle; use imbl::{vector, Vector}; use imbl_value::InternedString; use models::{HostId, PackageId, ServiceInterfaceId}; use patch_db::json_ptr::JsonPointer; +use serde::{Deserialize, Serialize}; use tracing::warn; +use ts_rs::TS; use crate::net::ssl::FullchainCertData; use crate::prelude::*; use crate::service::effects::context::EffectContext; use crate::service::effects::net::ssl::Algorithm; -use crate::service::rpc::CallbackHandle; +use crate::service::rpc::{CallbackHandle, CallbackId}; use crate::service::{Service, ServiceActorSeed}; use crate::util::collections::EqMap; @@ -272,6 +275,7 @@ impl CallbackHandler { } } pub async fn call(mut self, args: Vector) -> Result<(), Error> { + dbg!(eyre!("callback fired: {}", self.handle.is_active())); if let Some(seed) = self.seed.upgrade() { seed.persistent_container .callback(self.handle.take(), args) @@ -299,13 +303,29 @@ impl CallbackHandlers { } } -pub(super) fn clear_callbacks(context: EffectContext) -> Result<(), Error> { +#[derive(Debug, Clone, Serialize, Deserialize, TS, Parser)] +#[ts(type = "{ only: number[] } | { except: number[] }")] +#[ts(export)] +pub struct ClearCallbacksParams { + #[arg(long, conflicts_with = "except")] + pub only: Option>, + #[arg(long, conflicts_with = "only")] + pub except: Option>, +} + +pub(super) fn clear_callbacks( + context: EffectContext, + ClearCallbacksParams { only, except }: ClearCallbacksParams, +) -> Result<(), Error> { let context = context.deref()?; - context - .seed - .persistent_container - .state - .send_if_modified(|s| !std::mem::take(&mut s.callbacks).is_empty()); + let only = only.map(|only| only.into_iter().collect::>()); + let except = except.map(|except| except.into_iter().collect::>()); + context.seed.persistent_container.state.send_modify(|s| { + s.callbacks.retain(|cb| { + only.as_ref().map_or(true, |only| !only.contains(cb)) + && except.as_ref().map_or(true, |except| except.contains(cb)) + }) + }); context.seed.ctx.callbacks.gc(); Ok(()) } diff --git a/core/startos/src/service/effects/config.rs b/core/startos/src/service/effects/config.rs deleted file mode 100644 index 647d3e272..000000000 --- a/core/startos/src/service/effects/config.rs +++ /dev/null @@ -1,53 +0,0 @@ -use models::PackageId; - -use crate::service::effects::prelude::*; - -#[derive(Debug, Clone, Serialize, Deserialize, Parser, TS)] -#[serde(rename_all = "camelCase")] -#[ts(export)] -pub struct GetConfiguredParams { - #[ts(optional)] - package_id: Option, -} -pub async fn get_configured(context: EffectContext) -> Result { - let context = context.deref()?; - let peeked = context.seed.ctx.db.peek().await; - let package_id = &context.seed.id; - peeked - .as_public() - .as_package_data() - .as_idx(package_id) - .or_not_found(package_id)? - .as_status() - .as_configured() - .de() -} - -#[derive(Debug, Clone, Serialize, Deserialize, Parser, TS)] -#[serde(rename_all = "camelCase")] -#[ts(export)] -pub struct SetConfigured { - configured: bool, -} -pub async fn set_configured( - context: EffectContext, - SetConfigured { configured }: SetConfigured, -) -> Result<(), Error> { - let context = context.deref()?; - let package_id = &context.seed.id; - context - .seed - .ctx - .db - .mutate(|db| { - db.as_public_mut() - .as_package_data_mut() - .as_idx_mut(package_id) - .or_not_found(package_id)? - .as_status_mut() - .as_configured_mut() - .ser(&configured) - }) - .await?; - Ok(()) -} diff --git a/core/startos/src/service/effects/control.rs b/core/startos/src/service/effects/control.rs index 6b3c6f8a0..4eb897c37 100644 --- a/core/startos/src/service/effects/control.rs +++ b/core/startos/src/service/effects/control.rs @@ -1,9 +1,9 @@ use std::str::FromStr; use clap::builder::ValueParserFactory; +use models::FromStrParser; use crate::service::effects::prelude::*; -use crate::util::clap::FromStrParser; pub async fn restart( context: EffectContext, diff --git a/core/startos/src/service/effects/dependency.rs b/core/startos/src/service/effects/dependency.rs index 26582d061..ed43b7a9c 100644 --- a/core/startos/src/service/effects/dependency.rs +++ b/core/startos/src/service/effects/dependency.rs @@ -6,13 +6,13 @@ use clap::builder::ValueParserFactory; use exver::VersionRange; use imbl::OrdMap; use imbl_value::InternedString; -use itertools::Itertools; -use models::{HealthCheckId, PackageId, VersionString, VolumeId}; +use models::{FromStrParser, HealthCheckId, PackageId, ReplayId, VersionString, VolumeId}; use patch_db::json_ptr::JsonPointer; use tokio::process::Command; use crate::db::model::package::{ - CurrentDependencies, CurrentDependencyInfo, CurrentDependencyKind, ManifestPreference, + ActionRequestEntry, CurrentDependencies, CurrentDependencyInfo, CurrentDependencyKind, + ManifestPreference, }; use crate::disk::mount::filesystem::bind::Bind; use crate::disk::mount::filesystem::idmapped::IdMapped; @@ -20,7 +20,6 @@ use crate::disk::mount::filesystem::{FileSystem, MountType}; use crate::rpc_continuations::Guid; use crate::service::effects::prelude::*; use crate::status::health_check::NamedHealthCheckResult; -use crate::util::clap::FromStrParser; use crate::util::Invoke; use crate::volume::data_dir; @@ -113,6 +112,7 @@ pub async fn expose_for_dependents( context: EffectContext, ExposeForDependentsParams { paths }: ExposeForDependentsParams, ) -> Result<(), Error> { + // TODO Ok(()) } @@ -192,16 +192,11 @@ impl ValueParserFactory for DependencyRequirement { #[command(rename_all = "camelCase")] #[ts(export)] pub struct SetDependenciesParams { - #[serde(default)] - procedure_id: Guid, dependencies: Vec, } pub async fn set_dependencies( context: EffectContext, - SetDependenciesParams { - procedure_id, - dependencies, - }: SetDependenciesParams, + SetDependenciesParams { dependencies }: SetDependenciesParams, ) -> Result<(), Error> { let context = context.deref()?; let id = &context.seed.id; @@ -222,19 +217,6 @@ pub async fn set_dependencies( version_range, ), }; - let config_satisfied = - if let Some(dep_service) = &*context.seed.ctx.services.get(&dep_id).await { - context - .dependency_config( - procedure_id.clone(), - dep_id.clone(), - dep_service.get_config(procedure_id.clone()).await?.config, - ) - .await? - .is_none() - } else { - true - }; let info = CurrentDependencyInfo { title: context .seed @@ -251,7 +233,6 @@ pub async fn set_dependencies( .await?, kind, version_range, - config_satisfied, }; deps.insert(dep_id, info); } @@ -282,7 +263,8 @@ pub async fn get_dependencies(context: EffectContext) -> Result Result(match kind { + match kind { CurrentDependencyKind::Exists => { DependencyRequirement::Exists { id, version_range } } @@ -301,9 +283,9 @@ pub async fn get_dependencies(context: EffectContext) -> Result, - #[ts(type = "string | null")] - installed_version: Option, - #[ts(type = "string[]")] + installed_version: Option, satisfies: BTreeSet, is_running: bool, - config_satisfied: bool, + requested_actions: BTreeMap, #[ts(as = "BTreeMap::")] health_checks: OrdMap, } @@ -335,14 +315,14 @@ pub async fn check_dependencies( ) -> Result, Error> { let context = context.deref()?; let db = context.seed.ctx.db.peek().await; - let current_dependencies = db + let pde = db .as_public() .as_package_data() .as_idx(&context.seed.id) - .or_not_found(&context.seed.id)? - .as_current_dependencies() - .de()?; - let package_ids: Vec<_> = package_ids + .or_not_found(&context.seed.id)?; + let current_dependencies = pde.as_current_dependencies().de()?; + let requested_actions = pde.as_requested_actions().de()?; + let package_dependency_info: Vec<_> = package_ids .unwrap_or_else(|| current_dependencies.0.keys().cloned().collect()) .into_iter() .filter_map(|x| { @@ -350,18 +330,23 @@ pub async fn check_dependencies( Some((x, info)) }) .collect(); - let mut results = Vec::with_capacity(package_ids.len()); + let mut results = Vec::with_capacity(package_dependency_info.len()); - for (package_id, dependency_info) in package_ids { + for (package_id, dependency_info) in package_dependency_info { let title = dependency_info.title.clone(); let Some(package) = db.as_public().as_package_data().as_idx(&package_id) else { + let requested_actions = requested_actions + .iter() + .filter(|(_, v)| v.request.package_id == package_id) + .map(|(k, v)| (k.clone(), v.clone())) + .collect(); results.push(CheckDependenciesResult { package_id, title, installed_version: None, satisfies: BTreeSet::new(), is_running: false, - config_satisfied: false, + requested_actions, health_checks: Default::default(), }); continue; @@ -369,22 +354,27 @@ pub async fn check_dependencies( let manifest = package.as_state_info().as_manifest(ManifestPreference::New); let installed_version = manifest.as_version().de()?.into_version(); let satisfies = manifest.as_satisfies().de()?; - let installed_version = Some(installed_version.clone()); + let installed_version = Some(installed_version.clone().into()); let is_installed = true; - let status = package.as_status().as_main().de()?; + let status = package.as_status().de()?; let is_running = if is_installed { status.running() } else { false }; let health_checks = status.health().cloned().unwrap_or_default(); + let requested_actions = requested_actions + .iter() + .filter(|(_, v)| v.request.package_id == package_id) + .map(|(k, v)| (k.clone(), v.clone())) + .collect(); results.push(CheckDependenciesResult { package_id, title, installed_version, satisfies, is_running, - config_satisfied: dependency_info.config_satisfied, + requested_actions, health_checks, }); } diff --git a/core/startos/src/service/effects/health.rs b/core/startos/src/service/effects/health.rs index 9bf756d60..c95dea946 100644 --- a/core/startos/src/service/effects/health.rs +++ b/core/startos/src/service/effects/health.rs @@ -29,7 +29,6 @@ pub async fn set_health( .as_idx_mut(package_id) .or_not_found(package_id)? .as_status_mut() - .as_main_mut() .mutate(|main| { match main { MainStatus::Running { ref mut health, .. } diff --git a/core/startos/src/service/effects/mod.rs b/core/startos/src/service/effects/mod.rs index e85481e96..844f5646b 100644 --- a/core/startos/src/service/effects/mod.rs +++ b/core/startos/src/service/effects/mod.rs @@ -7,7 +7,6 @@ use crate::service::effects::context::EffectContext; mod action; pub mod callbacks; -mod config; pub mod context; mod control; mod dependency; @@ -26,34 +25,12 @@ pub fn handler() -> ParentHandler { from_fn(echo::).with_call_remote::(), ) // action - .subcommand( - "execute-action", - from_fn_async(action::execute_action).no_cli(), - ) - .subcommand( - "export-action", - from_fn_async(action::export_action).no_cli(), - ) - .subcommand( - "clear-actions", - from_fn_async(action::clear_actions).no_cli(), - ) + .subcommand("action", action::action_api::()) // callbacks .subcommand( "clear-callbacks", from_fn(callbacks::clear_callbacks).no_cli(), ) - // config - .subcommand( - "get-configured", - from_fn_async(config::get_configured).no_cli(), - ) - .subcommand( - "set-configured", - from_fn_async(config::set_configured) - .no_display() - .with_call_remote::(), - ) // control .subcommand( "restart", diff --git a/core/startos/src/service/effects/net/bind.rs b/core/startos/src/service/effects/net/bind.rs index ba273323a..40bc550cb 100644 --- a/core/startos/src/service/effects/net/bind.rs +++ b/core/startos/src/service/effects/net/bind.rs @@ -1,6 +1,6 @@ use models::{HostId, PackageId}; -use crate::net::host::binding::{BindOptions, LanInfo}; +use crate::net::host::binding::{BindId, BindOptions, LanInfo}; use crate::net::host::HostKind; use crate::service::effects::prelude::*; @@ -28,10 +28,20 @@ pub async fn bind( svc.bind(kind, id, internal_port, options).await } -pub async fn clear_bindings(context: EffectContext) -> Result<(), Error> { +#[derive(Debug, Clone, Serialize, Deserialize, TS, Parser)] +#[ts(export)] +#[serde(rename_all = "camelCase")] +pub struct ClearBindingsParams { + pub except: Vec, +} + +pub async fn clear_bindings( + context: EffectContext, + ClearBindingsParams { except }: ClearBindingsParams, +) -> Result<(), Error> { let context = context.deref()?; let mut svc = context.seed.persistent_container.net_service.lock().await; - svc.clear_bindings().await?; + svc.clear_bindings(except.into_iter().collect()).await?; Ok(()) } diff --git a/core/startos/src/service/effects/net/interface.rs b/core/startos/src/service/effects/net/interface.rs index 6cd4cd4c9..44258c36a 100644 --- a/core/startos/src/service/effects/net/interface.rs +++ b/core/startos/src/service/effects/net/interface.rs @@ -165,7 +165,17 @@ pub async fn list_service_interfaces( Ok(res) } -pub async fn clear_service_interfaces(context: EffectContext) -> Result<(), Error> { +#[derive(Debug, Clone, Serialize, Deserialize, TS, Parser)] +#[ts(export)] +#[serde(rename_all = "camelCase")] +pub struct ClearServiceInterfacesParams { + pub except: Vec, +} + +pub async fn clear_service_interfaces( + context: EffectContext, + ClearServiceInterfacesParams { except }: ClearServiceInterfacesParams, +) -> Result<(), Error> { let context = context.deref()?; let package_id = context.seed.id.clone(); @@ -179,7 +189,7 @@ pub async fn clear_service_interfaces(context: EffectContext) -> Result<(), Erro .as_idx_mut(&package_id) .or_not_found(&package_id)? .as_service_interfaces_mut() - .ser(&Default::default()) + .mutate(|s| Ok(s.retain(|id, _| except.contains(id)))) }) .await } diff --git a/core/startos/src/service/effects/store.rs b/core/startos/src/service/effects/store.rs index 6c12b425e..1d4a07086 100644 --- a/core/startos/src/service/effects/store.rs +++ b/core/startos/src/service/effects/store.rs @@ -26,6 +26,7 @@ pub async fn get_store( callback, }: GetStoreParams, ) -> Result { + dbg!(&callback); let context = context.deref()?; let peeked = context.seed.ctx.db.peek().await; let package_id = package_id.unwrap_or(context.seed.id.clone()); @@ -33,8 +34,9 @@ pub async fn get_store( .as_private() .as_package_stores() .as_idx(&package_id) - .or_not_found(&package_id)? - .de()?; + .map(|s| s.de()) + .transpose()? + .unwrap_or_default(); if let Some(callback) = callback { let callback = callback.register(&context.seed.persistent_container); @@ -45,10 +47,7 @@ pub async fn get_store( ); } - Ok(path - .get(&value) - .ok_or_else(|| Error::new(eyre!("Did not find value at path"), ErrorKind::NotFound))? - .clone()) + Ok(path.get(&value).cloned().unwrap_or_default()) } #[derive(Debug, Clone, Serialize, Deserialize, TS)] diff --git a/core/startos/src/service/mod.rs b/core/startos/src/service/mod.rs index 55f76fd14..1e61b1191 100644 --- a/core/startos/src/service/mod.rs +++ b/core/startos/src/service/mod.rs @@ -1,11 +1,12 @@ +use std::collections::{BTreeMap, BTreeSet}; +use std::ffi::OsString; use std::io::IsTerminal; use std::ops::Deref; use std::os::unix::process::ExitStatusExt; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::process::Stdio; use std::sync::{Arc, Weak}; use std::time::Duration; -use std::{ffi::OsString, path::PathBuf}; use axum::extract::ws::WebSocket; use chrono::{DateTime, Utc}; @@ -15,7 +16,7 @@ use futures::stream::FusedStream; use futures::{SinkExt, StreamExt, TryStreamExt}; use imbl_value::{json, InternedString}; use itertools::Itertools; -use models::{ImageId, PackageId, ProcedureName}; +use models::{ActionId, ImageId, PackageId, ProcedureName}; use nix::sys::signal::Signal; use persistent_container::{PersistentContainer, Subcontainer}; use rpc_toolkit::{from_fn_async, CallRemoteHandler, Empty, HandlerArgs, HandlerFor}; @@ -39,6 +40,7 @@ use crate::prelude::*; use crate::progress::{NamedProgress, Progress}; use crate::rpc_continuations::{Guid, RpcContinuation}; use crate::s9pk::S9pk; +use crate::service::action::update_requested_actions; use crate::service::service_map::InstallProgressHandles; use crate::util::actor::concurrent::ConcurrentActor; use crate::util::io::{create_file, AsyncReadStream}; @@ -48,11 +50,9 @@ use crate::util::Never; use crate::volume::data_dir; use crate::CAP_1_KiB; -mod action; +pub mod action; pub mod cli; -mod config; mod control; -mod dependencies; pub mod effects; pub mod persistent_container; mod properties; @@ -97,7 +97,7 @@ impl ServiceRef { .persistent_container .execute::( Guid::new(), - ProcedureName::Uninit, + ProcedureName::PackageUninit, to_value(&target_version)?, None, ) // TODO timeout @@ -257,7 +257,7 @@ impl Service { tokio::fs::create_dir_all(&path).await?; } } - let start_stop = if i.as_status().as_main().de()?.running() { + let start_stop = if i.as_status().de()?.running() { StartStop::Start } else { StartStop::Stop @@ -429,12 +429,13 @@ impl Service { .clone(), ); } + let procedure_id = Guid::new(); service .seed .persistent_container .execute::( - Guid::new(), - ProcedureName::Init, + procedure_id.clone(), + ProcedureName::PackageInit, to_value(&src_version)?, None, ) // TODO timeout @@ -445,16 +446,60 @@ impl Service { progress.progress.complete(); tokio::task::yield_now().await; } + + let peek = ctx.db.peek().await; + let mut action_input: BTreeMap = BTreeMap::new(); + let requested_actions: BTreeSet<_> = peek + .as_public() + .as_package_data() + .as_entries()? + .into_iter() + .map(|(_, pde)| { + Ok(pde + .as_requested_actions() + .as_entries()? + .into_iter() + .map(|(_, r)| { + Ok::<_, Error>(if r.as_request().as_package_id().de()? == manifest.id { + Some(r.as_request().as_action_id().de()?) + } else { + None + }) + }) + .filter_map_ok(|a| a)) + }) + .flatten_ok() + .map(|a| a.and_then(|a| a)) + .try_collect()?; + for action_id in requested_actions { + if let Some(input) = service + .get_action_input(procedure_id.clone(), action_id.clone()) + .await? + .and_then(|i| i.value) + { + action_input.insert(action_id, input); + } + } ctx.db - .mutate(|d| { - let entry = d + .mutate(|db| { + for (action_id, input) in &action_input { + for (_, pde) in db.as_public_mut().as_package_data_mut().as_entries_mut()? { + pde.as_requested_actions_mut().mutate(|requested_actions| { + Ok(update_requested_actions( + requested_actions, + &manifest.id, + action_id, + input, + false, + )) + })?; + } + } + let entry = db .as_public_mut() .as_package_data_mut() .as_idx_mut(&manifest.id) .or_not_found(&manifest.id)?; - if !manifest.has_config { - entry.as_status_mut().as_configured_mut().ser(&true)?; - } entry .as_state_info_mut() .ser(&PackageState::Installed(InstalledState { manifest }))?; diff --git a/core/startos/src/service/persistent_container.rs b/core/startos/src/service/persistent_container.rs index ff9d74009..3f85a95d5 100644 --- a/core/startos/src/service/persistent_container.rs +++ b/core/startos/src/service/persistent_container.rs @@ -379,7 +379,11 @@ impl PersistentContainer { )); } - self.rpc_client.request(rpc::Init, Empty {}).await?; + self.rpc_client + .request(rpc::Init, Empty {}) + .await + .map_err(Error::from) + .log_err(); self.state.send_modify(|s| s.rt_initialized = true); @@ -548,7 +552,7 @@ impl PersistentContainer { impl Drop for PersistentContainer { fn drop(&mut self) { if let Some(destroy) = self.destroy() { - tokio::spawn(async move { destroy.await.unwrap() }); + tokio::spawn(async move { destroy.await.log_err() }); } } } diff --git a/core/startos/src/service/rpc.rs b/core/startos/src/service/rpc.rs index 25d8fb067..f008de5c7 100644 --- a/core/startos/src/service/rpc.rs +++ b/core/startos/src/service/rpc.rs @@ -1,10 +1,12 @@ use std::collections::BTreeSet; +use std::str::FromStr; use std::sync::{Arc, Weak}; use std::time::Duration; +use clap::builder::ValueParserFactory; use imbl::Vector; use imbl_value::Value; -use models::ProcedureName; +use models::{FromStrParser, ProcedureName}; use rpc_toolkit::yajrc::RpcMethod; use rpc_toolkit::Empty; use ts_rs::TS; @@ -153,6 +155,11 @@ impl serde::Serialize for Sandbox { pub struct CallbackId(u64); impl CallbackId { pub fn register(self, container: &PersistentContainer) -> CallbackHandle { + dbg!(eyre!( + "callback {} registered for {}", + self.0, + container.s9pk.as_manifest().id + )); let this = Arc::new(self); let res = Arc::downgrade(&this); container @@ -161,6 +168,18 @@ impl CallbackId { CallbackHandle(res) } } +impl FromStr for CallbackId { + type Err = Error; + fn from_str(s: &str) -> Result { + u64::from_str(s).map_err(Error::from).map(Self) + } +} +impl ValueParserFactory for CallbackId { + type Parser = FromStrParser; + fn value_parser() -> Self::Parser { + FromStrParser::new() + } +} pub struct CallbackHandle(Weak); impl CallbackHandle { diff --git a/core/startos/src/service/service_actor.rs b/core/startos/src/service/service_actor.rs index 0839afc0b..b343e513e 100644 --- a/core/startos/src/service/service_actor.rs +++ b/core/startos/src/service/service_actor.rs @@ -49,7 +49,7 @@ async fn service_actor_loop( .db .mutate(|d| { if let Some(i) = d.as_public_mut().as_package_data_mut().as_idx_mut(&id) { - let previous = i.as_status().as_main().de()?; + let previous = i.as_status().de()?; let main_status = match &kinds { ServiceStateKinds { transition_state: Some(TransitionKind::Restarting), @@ -89,7 +89,7 @@ async fn service_actor_loop( .. } => MainStatus::Stopped, }; - i.as_status_mut().as_main_mut().ser(&main_status)?; + i.as_status_mut().ser(&main_status)?; } Ok(()) }) diff --git a/core/startos/src/service/service_map.rs b/core/startos/src/service/service_map.rs index 0e6a959ae..ebe45fcc5 100644 --- a/core/startos/src/service/service_map.rs +++ b/core/startos/src/service/service_map.rs @@ -23,7 +23,7 @@ use crate::s9pk::manifest::PackageId; use crate::s9pk::merkle_archive::source::FileSource; use crate::s9pk::S9pk; use crate::service::{LoadDisposition, Service, ServiceRef}; -use crate::status::{MainStatus, Status}; +use crate::status::MainStatus; use crate::util::serde::Pem; pub type DownloadInstallFuture = BoxFuture<'static, Result>; @@ -174,16 +174,14 @@ impl ServiceMap { PackageState::Installing(installing) }, data_version: None, - status: Status { - configured: false, - main: MainStatus::Stopped, - }, + status: MainStatus::Stopped, registry: None, developer_key: Pem::new(developer_key), icon, last_backup: None, current_dependencies: Default::default(), actions: Default::default(), + requested_actions: Default::default(), service_interfaces: Default::default(), hosts: Default::default(), store_exposed_dependents: Default::default(), diff --git a/core/startos/src/service/transition/backup.rs b/core/startos/src/service/transition/backup.rs index d8606f534..0d4116078 100644 --- a/core/startos/src/service/transition/backup.rs +++ b/core/startos/src/service/transition/backup.rs @@ -9,8 +9,7 @@ use super::TempDesiredRestore; use crate::disk::mount::filesystem::ReadWrite; use crate::prelude::*; use crate::rpc_continuations::Guid; -use crate::service::config::GetConfig; -use crate::service::dependencies::DependencyConfig; +use crate::service::action::GetActionInput; use crate::service::transition::{TransitionKind, TransitionState}; use crate::service::ServiceActor; use crate::util::actor::background::BackgroundJobQueue; @@ -23,9 +22,7 @@ pub(in crate::service) struct Backup { impl Handler for ServiceActor { type Response = Result>, Error>; fn conflicts_with(_: &Backup) -> ConflictBuilder { - ConflictBuilder::everything() - .except::() - .except::() + ConflictBuilder::everything().except::() } async fn handle( &mut self, diff --git a/core/startos/src/service/transition/restart.rs b/core/startos/src/service/transition/restart.rs index 108e232ad..27bef0b91 100644 --- a/core/startos/src/service/transition/restart.rs +++ b/core/startos/src/service/transition/restart.rs @@ -3,8 +3,7 @@ use futures::FutureExt; use super::TempDesiredRestore; use crate::prelude::*; use crate::rpc_continuations::Guid; -use crate::service::config::GetConfig; -use crate::service::dependencies::DependencyConfig; +use crate::service::action::GetActionInput; use crate::service::transition::{TransitionKind, TransitionState}; use crate::service::{Service, ServiceActor}; use crate::util::actor::background::BackgroundJobQueue; @@ -15,9 +14,7 @@ pub(super) struct Restart; impl Handler for ServiceActor { type Response = (); fn conflicts_with(_: &Restart) -> ConflictBuilder { - ConflictBuilder::everything() - .except::() - .except::() + ConflictBuilder::everything().except::() } async fn handle(&mut self, _: Guid, _: Restart, jobs: &BackgroundJobQueue) -> Self::Response { // So Need a handle to just a single field in the state diff --git a/core/startos/src/ssh.rs b/core/startos/src/ssh.rs index b97fba3e4..728fd6480 100644 --- a/core/startos/src/ssh.rs +++ b/core/startos/src/ssh.rs @@ -5,6 +5,7 @@ use clap::builder::ValueParserFactory; use clap::Parser; use color_eyre::eyre::eyre; use imbl_value::InternedString; +use models::FromStrParser; use rpc_toolkit::{from_fn_async, Context, Empty, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; use tracing::instrument; @@ -12,7 +13,6 @@ use ts_rs::TS; use crate::context::{CliContext, RpcContext}; use crate::prelude::*; -use crate::util::clap::FromStrParser; use crate::util::io::create_file; use crate::util::serde::{display_serializable, HandlerExtSerde, WithIoFormat}; diff --git a/core/startos/src/status/health_check.rs b/core/startos/src/status/health_check.rs index 1b1e2a7b6..861954d29 100644 --- a/core/startos/src/status/health_check.rs +++ b/core/startos/src/status/health_check.rs @@ -1,12 +1,11 @@ use std::str::FromStr; use clap::builder::ValueParserFactory; +use models::FromStrParser; pub use models::HealthCheckId; use serde::{Deserialize, Serialize}; use ts_rs::TS; -use crate::util::clap::FromStrParser; - #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, TS)] #[serde(rename_all = "camelCase")] pub struct NamedHealthCheckResult { diff --git a/core/startos/src/status/mod.rs b/core/startos/src/status/mod.rs index c10a7b89f..e45085826 100644 --- a/core/startos/src/status/mod.rs +++ b/core/startos/src/status/mod.rs @@ -11,17 +11,9 @@ use crate::service::start_stop::StartStop; use crate::status::health_check::NamedHealthCheckResult; pub mod health_check; -#[derive(Clone, Debug, Deserialize, Serialize, HasModel, TS)] -#[serde(rename_all = "camelCase")] -#[model = "Model"] -#[ts(export)] -pub struct Status { - pub configured: bool, - pub main: MainStatus, -} #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, TS)] -#[serde(tag = "status")] +#[serde(tag = "main")] #[serde(rename_all = "camelCase")] #[serde(rename_all_fields = "camelCase")] pub enum MainStatus { diff --git a/core/startos/src/util/io.rs b/core/startos/src/util/io.rs index e3059c862..c3bf83d00 100644 --- a/core/startos/src/util/io.rs +++ b/core/startos/src/util/io.rs @@ -442,6 +442,13 @@ impl BackTrackingIO { }, } } + pub fn read_buffer(&self) -> &[u8] { + match &self.buffer { + BTBuffer::NotBuffering => &[], + BTBuffer::Buffering { read, .. } => read, + BTBuffer::Rewound { read } => read.remaining_slice(), + } + } #[must_use] pub fn stop_buffering(&mut self) -> Vec { match std::mem::take(&mut self.buffer) { @@ -512,6 +519,28 @@ impl AsyncRead for BackTrackingIO { } } } +impl std::io::Read for BackTrackingIO { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + match &mut self.buffer { + BTBuffer::Buffering { read, .. } => { + let n = self.io.read(buf)?; + read.extend_from_slice(&buf[..n]); + Ok(n) + } + BTBuffer::NotBuffering => self.io.read(buf), + BTBuffer::Rewound { read } => { + let mut ready = false; + if (read.position() as usize) < read.get_ref().len() { + let n = std::io::Read::read(read, buf)?; + if n != 0 { + return Ok(n); + } + } + self.io.read(buf) + } + } + } +} impl AsyncWrite for BackTrackingIO { fn is_write_vectored(&self) -> bool { @@ -869,7 +898,7 @@ impl Drop for TmpDir { if self.path.exists() { let path = std::mem::take(&mut self.path); tokio::spawn(async move { - tokio::fs::remove_dir_all(&path).await.unwrap(); + tokio::fs::remove_dir_all(&path).await.log_err(); }); } } diff --git a/core/startos/src/util/mod.rs b/core/startos/src/util/mod.rs index fab0b127d..f26bfa6b5 100644 --- a/core/startos/src/util/mod.rs +++ b/core/startos/src/util/mod.rs @@ -36,7 +36,6 @@ use crate::util::serde::{deserialize_from_str, serialize_display}; use crate::{Error, ErrorKind, ResultExt as _}; pub mod actor; -pub mod clap; pub mod collections; pub mod cpupower; pub mod crypto; @@ -568,7 +567,7 @@ pub struct FileLock(#[allow(unused)] OwnedMutexGuard<()>, Option>); impl Drop for FileLock { fn drop(&mut self) { if let Some(fd_lock) = self.1.take() { - tokio::task::spawn_blocking(|| fd_lock.unlock(true).map_err(|(_, e)| e).unwrap()); + tokio::task::spawn_blocking(|| fd_lock.unlock(true).map_err(|(_, e)| e).log_err()); } } } diff --git a/core/startos/src/util/serde.rs b/core/startos/src/util/serde.rs index 88d7bfc11..8f4bf4c15 100644 --- a/core/startos/src/util/serde.rs +++ b/core/startos/src/util/serde.rs @@ -1,4 +1,3 @@ -use std::any::Any; use std::collections::VecDeque; use std::marker::PhantomData; use std::ops::Deref; @@ -9,6 +8,7 @@ use clap::builder::ValueParserFactory; use clap::{ArgMatches, CommandFactory, FromArgMatches}; use color_eyre::eyre::eyre; use imbl::OrdMap; +use models::FromStrParser; use openssl::pkey::{PKey, Private}; use openssl::x509::X509; use rpc_toolkit::{ @@ -17,12 +17,10 @@ use rpc_toolkit::{ use serde::de::DeserializeOwned; use serde::ser::{SerializeMap, SerializeSeq}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use serde_json::Value; use ts_rs::TS; use super::IntoDoubleEndedIterator; use crate::prelude::*; -use crate::util::clap::FromStrParser; use crate::util::Apply; pub fn deserialize_from_str< @@ -272,7 +270,7 @@ impl std::fmt::Display for IoFormat { impl std::str::FromStr for IoFormat { type Err = Error; fn from_str(s: &str) -> Result { - serde_json::from_value(Value::String(s.to_owned())) + serde_json::from_value(serde_json::Value::String(s.to_owned())) .with_kind(crate::ErrorKind::Deserialization) } } @@ -566,7 +564,7 @@ where } } -#[derive(Deserialize, Serialize, TS)] +#[derive(Deserialize, Serialize, TS, Clone)] pub struct StdinDeserializable(pub T); impl Default for StdinDeserializable where @@ -1358,3 +1356,19 @@ impl Serialize for MaybeUtf8String { } } } + +pub fn is_partial_of(partial: &Value, full: &Value) -> bool { + match (partial, full) { + (Value::Object(partial), Value::Object(full)) => partial.iter().all(|(k, v)| { + if let Some(v_full) = full.get(k) { + is_partial_of(v, v_full) + } else { + false + } + }), + (Value::Array(partial), Value::Array(full)) => partial + .iter() + .all(|v| full.iter().any(|v_full| is_partial_of(v, v_full))), + (_, _) => partial == full, + } +} diff --git a/sdk/.gitignore b/sdk/.gitignore index a7ca92b2d..1ac0f02e6 100644 --- a/sdk/.gitignore +++ b/sdk/.gitignore @@ -1,5 +1,6 @@ -.vscode dist/ -node_modules/ -lib/coverage -lib/test/output.ts \ No newline at end of file +baseDist/ +base/lib/coverage +base/lib/node_modules +package/lib/coverage +package/lib/node_modules \ No newline at end of file diff --git a/sdk/.prettierignore b/sdk/.prettierignore index 19b24bbe8..149281301 100644 --- a/sdk/.prettierignore +++ b/sdk/.prettierignore @@ -1 +1 @@ -/lib/exver/exver.ts \ No newline at end of file +/base/lib/exver/exver.ts \ No newline at end of file diff --git a/sdk/Makefile b/sdk/Makefile index 660a476c4..bc025709d 100644 --- a/sdk/Makefile +++ b/sdk/Makefile @@ -1,48 +1,75 @@ -TS_FILES := $(shell git ls-files lib) lib/test/output.ts +PACKAGE_TS_FILES := $(shell git ls-files package/lib) package/lib/test/output.ts +BASE_TS_FILES := $(shell git ls-files base/lib) package/lib/test/output.ts version = $(shell git tag --sort=committerdate | tail -1) -.PHONY: test clean bundle fmt buildOutput check +.PHONY: test base/test package/test clean bundle fmt buildOutput check all: bundle -test: $(TS_FILES) lib/test/output.ts - npm test +package/test: $(PACKAGE_TS_FILES) package/lib/test/output.ts package/node_modules base/node_modules + cd package && npm test + +base/test: $(BASE_TS_FILES) base/node_modules + cd base && npm test + +test: base/test package/test clean: + rm -rf base/node_modules rm -rf dist - rm -f lib/test/output.ts - rm -rf node_modules + rm -rf baseDist + rm -f package/lib/test/output.ts + rm -rf package/node_modules -lib/test/output.ts: node_modules lib/test/makeOutput.ts scripts/oldSpecToBuilder.ts - npm run buildOutput +package/lib/test/output.ts: package/node_modules package/lib/test/makeOutput.ts package/scripts/oldSpecToBuilder.ts + cd package && npm run buildOutput -bundle: dist | test fmt +bundle: dist baseDist | test fmt touch dist -lib/exver/exver.ts: node_modules lib/exver/exver.pegjs - npx peggy --allowed-start-rules '*' --plugin ./node_modules/ts-pegjs/dist/tspegjs -o lib/exver/exver.ts lib/exver/exver.pegjs +base/lib/exver/exver.ts: base/node_modules base/lib/exver/exver.pegjs + cd base && npm run peggy -dist: $(TS_FILES) package.json node_modules README.md LICENSE - npx tsc - npx tsc --project tsconfig-cjs.json - cp package.json dist/package.json - cp README.md dist/README.md - cp LICENSE dist/LICENSE +baseDist: $(PACKAGE_TS_FILES) $(BASE_TS_FILES) base/package.json base/node_modules base/README.md base/LICENSE + (cd base && npm run tsc) + rsync -ac base/node_modules baseDist/ + cp base/package.json baseDist/package.json + cp base/README.md baseDist/README.md + cp base/LICENSE baseDist/LICENSE + touch baseDist + +dist: $(PACKAGE_TS_FILES) $(BASE_TS_FILES) package/package.json package/.npmignore package/node_modules package/README.md package/LICENSE + (cd package && npm run tsc) + rsync -ac package/node_modules dist/ + cp package/.npmignore dist/.npmignore + cp package/package.json dist/package.json + cp package/README.md dist/README.md + cp package/LICENSE dist/LICENSE touch dist full-bundle: bundle check: + cd package + npm run check + cd ../base npm run check -fmt: node_modules +fmt: package/node_modules base/node_modules npx prettier . "**/*.ts" --write -node_modules: package.json - npm ci -publish: bundle package.json README.md LICENSE - cd dist && npm publish --access=public +package/node_modules: package/package.json + cd package && npm ci + +base/node_modules: base/package.json + cd base && npm ci + +node_modules: package/node_modules base/node_modules + +publish: bundle package/package.json README.md LICENSE + cd dist + npm publish --access=public link: bundle cd dist && npm link diff --git a/sdk/base/.gitignore b/sdk/base/.gitignore new file mode 100644 index 000000000..a7ca92b2d --- /dev/null +++ b/sdk/base/.gitignore @@ -0,0 +1,5 @@ +.vscode +dist/ +node_modules/ +lib/coverage +lib/test/output.ts \ No newline at end of file diff --git a/sdk/LICENSE b/sdk/base/LICENSE similarity index 100% rename from sdk/LICENSE rename to sdk/base/LICENSE diff --git a/sdk/base/README.md b/sdk/base/README.md new file mode 100644 index 000000000..33bf5ff9d --- /dev/null +++ b/sdk/base/README.md @@ -0,0 +1 @@ +# See ../package/README.md diff --git a/sdk/jest.config.js b/sdk/base/jest.config.js similarity index 100% rename from sdk/jest.config.js rename to sdk/base/jest.config.js diff --git a/sdk/base/lib/Effects.ts b/sdk/base/lib/Effects.ts new file mode 100644 index 000000000..3dab037c0 --- /dev/null +++ b/sdk/base/lib/Effects.ts @@ -0,0 +1,191 @@ +import { + ActionId, + ActionInput, + ActionMetadata, + SetMainStatus, + DependencyRequirement, + CheckDependenciesResult, + SetHealth, + BindParams, + HostId, + LanInfo, + Host, + ExportServiceInterfaceParams, + ServiceInterface, + ActionRequest, + RequestActionParams, +} from "./osBindings" +import { StorePath } from "./util/PathBuilder" +import { + PackageId, + Dependencies, + ServiceInterfaceId, + SmtpValue, + ActionResult, +} from "./types" +import { UrlString } from "./util/getServiceInterface" + +/** Used to reach out from the pure js runtime */ + +export type Effects = { + constRetry: () => void + clearCallbacks: ( + options: { only: number[] } | { except: number[] }, + ) => Promise + + // action + action: { + /** Define an action that can be invoked by a user or service */ + export(options: { id: ActionId; metadata: ActionMetadata }): Promise + /** Remove all exported actions */ + clear(options: { except: ActionId[] }): Promise + getInput(options: { + packageId?: PackageId + actionId: ActionId + }): Promise + run>(options: { + packageId?: PackageId + actionId: ActionId + input?: Input + }): Promise + request>( + options: RequestActionParams, + ): Promise + clearRequests( + options: { only: ActionId[] } | { except: ActionId[] }, + ): Promise + } + + // control + /** restart this service's main function */ + restart(): Promise + /** stop this service's main function */ + shutdown(): Promise + /** indicate to the host os what runstate the service is in */ + setMainStatus(options: SetMainStatus): Promise + + // dependency + /** Set the dependencies of what the service needs, usually run during the inputSpec action as a best practice */ + setDependencies(options: { dependencies: Dependencies }): Promise + /** Get the list of the dependencies, both the dynamic set by the effect of setDependencies and the end result any required in the manifest */ + getDependencies(): Promise + /** Test whether current dependency requirements are satisfied */ + checkDependencies(options: { + packageIds?: PackageId[] + }): Promise + /** mount a volume of a dependency */ + mount(options: { + location: string + target: { + packageId: string + volumeId: string + subpath: string | null + readonly: boolean + } + }): Promise + /** Returns a list of the ids of all installed packages */ + getInstalledPackages(): Promise + /** grants access to certain paths in the store to dependents */ + exposeForDependents(options: { paths: string[] }): Promise + + // health + /** sets the result of a health check */ + setHealth(o: SetHealth): Promise + + // subcontainer + subcontainer: { + /** A low level api used by SubContainer */ + createFs(options: { + imageId: string + name: string | null + }): Promise<[string, string]> + /** A low level api used by SubContainer */ + destroyFs(options: { guid: string }): Promise + } + + // net + // bind + /** Creates a host connected to the specified port with the provided options */ + bind(options: BindParams): Promise + /** Get the port address for a service */ + getServicePortForward(options: { + packageId?: PackageId + hostId: HostId + internalPort: number + }): Promise + /** Removes all network bindings, called in the setupInputSpec */ + clearBindings(options: { + except: { id: HostId; internalPort: number }[] + }): Promise + // host + /** Returns information about the specified host, if it exists */ + getHostInfo(options: { + packageId?: PackageId + hostId: HostId + callback?: () => void + }): Promise + /** Returns the primary url that a user has selected for a host, if it exists */ + getPrimaryUrl(options: { + packageId?: PackageId + hostId: HostId + callback?: () => void + }): Promise + /** Returns the IP address of the container */ + getContainerIp(): Promise + // interface + /** Creates an interface bound to a specific host and port to show to the user */ + exportServiceInterface(options: ExportServiceInterfaceParams): Promise + /** Returns an exported service interface */ + getServiceInterface(options: { + packageId?: PackageId + serviceInterfaceId: ServiceInterfaceId + callback?: () => void + }): Promise + /** Returns all exported service interfaces for a package */ + listServiceInterfaces(options: { + packageId?: PackageId + callback?: () => void + }): Promise> + /** Removes all service interfaces */ + clearServiceInterfaces(options: { + except: ServiceInterfaceId[] + }): Promise + // ssl + /** Returns a PEM encoded fullchain for the hostnames specified */ + getSslCertificate: (options: { + hostnames: string[] + algorithm?: "ecdsa" | "ed25519" + callback?: () => void + }) => Promise<[string, string, string]> + /** Returns a PEM encoded private key corresponding to the certificate for the hostnames specified */ + getSslKey: (options: { + hostnames: string[] + algorithm?: "ecdsa" | "ed25519" + }) => Promise + + // store + store: { + /** Get a value in a json like data, can be observed and subscribed */ + get(options: { + /** If there is no packageId it is assumed the current package */ + packageId?: string + /** The path defaults to root level, using the [JsonPath](https://jsonpath.com/) */ + path: StorePath + callback?: () => void + }): Promise + /** Used to store values that can be accessed and subscribed to */ + set(options: { + /** Sets the value for the wrapper at the path, it will override, using the [JsonPath](https://jsonpath.com/) */ + path: StorePath + value: ExtractStore + }): Promise + } + /** sets the version that this service's data has been migrated to */ + setDataVersion(options: { version: string }): Promise + /** returns the version that this service's data has been migrated to */ + getDataVersion(): Promise + + // system + /** Returns globally configured SMTP settings, if they exist */ + getSystemSmtp(options: { callback?: () => void }): Promise +} diff --git a/sdk/base/lib/actions/index.ts b/sdk/base/lib/actions/index.ts new file mode 100644 index 000000000..cebb03ee1 --- /dev/null +++ b/sdk/base/lib/actions/index.ts @@ -0,0 +1,65 @@ +import * as T from "../types" +import * as IST from "../actions/input/inputSpecTypes" + +export type RunActionInput = + | Input + | ((prev?: { spec: IST.InputSpec; value: Input | null }) => Input) + +export const runAction = async < + Input extends Record, +>(options: { + effects: T.Effects + // packageId?: T.PackageId + actionId: T.ActionId + input?: RunActionInput +}) => { + if (options.input) { + if (options.input instanceof Function) { + const prev = await options.effects.action.getInput({ + // packageId: options.packageId, + actionId: options.actionId, + }) + const input = options.input( + prev + ? { spec: prev.spec as IST.InputSpec, value: prev.value as Input } + : undefined, + ) + return options.effects.action.run({ + // packageId: options.packageId, + actionId: options.actionId, + input, + }) + } else { + return options.effects.action.run({ + // packageId: options.packageId, + actionId: options.actionId, + input: options.input, + }) + } + } else { + return options.effects.action.run({ + // packageId: options.packageId, + actionId: options.actionId, + }) + } +} + +// prettier-ignore +export type ActionRequest> = + T extends { when: { condition: "input-not-matches" } } + ? (T extends { input: T.ActionRequestInput } ? T : "input is required for condition 'input-not-matches'") + : T + +export const requestAction = < + T extends Omit, +>(options: { + effects: T.Effects + request: ActionRequest & { replayId?: string; packageId: T.PackageId } +}) => { + const request = options.request + const req = { + ...request, + replayId: request.replayId || `${request.packageId}:${request.actionId}`, + } + return options.effects.action.request(req) +} diff --git a/sdk/lib/config/builder/index.ts b/sdk/base/lib/actions/input/builder/index.ts similarity index 51% rename from sdk/lib/config/builder/index.ts rename to sdk/base/lib/actions/input/builder/index.ts index 6b6ddc730..618c4856f 100644 --- a/sdk/lib/config/builder/index.ts +++ b/sdk/base/lib/actions/input/builder/index.ts @@ -1,6 +1,6 @@ -import { Config } from "./config" +import { InputSpec } from "./inputSpec" import { List } from "./list" import { Value } from "./value" import { Variants } from "./variants" -export { Config, List, Value, Variants } +export { InputSpec as InputSpec, List, Value, Variants } diff --git a/sdk/lib/config/builder/config.ts b/sdk/base/lib/actions/input/builder/inputSpec.ts similarity index 73% rename from sdk/lib/config/builder/config.ts rename to sdk/base/lib/actions/input/builder/inputSpec.ts index c30f37890..288ce8b3e 100644 --- a/sdk/lib/config/builder/config.ts +++ b/sdk/base/lib/actions/input/builder/inputSpec.ts @@ -1,7 +1,7 @@ -import { ValueSpec } from "../configTypes" +import { ValueSpec } from "../inputSpecTypes" import { Value } from "./value" -import { _ } from "../../util" -import { Effects } from "../../types" +import { _ } from "../../../util" +import { Effects } from "../../../Effects" import { Parser, object } from "ts-matches" export type LazyBuildOptions = { @@ -12,20 +12,20 @@ export type LazyBuild = ( ) => Promise | ExpectedOut // prettier-ignore -export type ExtractConfigType | Config, any> | Config, never>> = - A extends Config | Config ? B : +export type ExtractInputSpecType | InputSpec, any> | InputSpec, never>> = + A extends InputSpec | InputSpec ? B : A -export type ConfigSpecOf, Store = never> = { +export type InputSpecOf, Store = never> = { [K in keyof A]: Value } export type MaybeLazyValues = LazyBuild | A /** - * Configs are the specs that are used by the os configuration form for this service. - * Here is an example of a simple configuration + * InputSpecs are the specs that are used by the os input specification form for this service. + * Here is an example of a simple input specification ```ts - const smallConfig = Config.of({ + const smallInputSpec = InputSpec.of({ test: Value.boolean({ name: "Test", description: "This is the description for the test", @@ -35,17 +35,17 @@ export type MaybeLazyValues = LazyBuild | A }); ``` - The idea of a config is that now the form is going to ask for + The idea of an inputSpec is that now the form is going to ask for Test: [ ] and the value is going to be checked as a boolean. There are more complex values like selects, lists, and objects. See {@link Value} - Also, there is the ability to get a validator/parser from this config spec. + Also, there is the ability to get a validator/parser from this inputSpec spec. ```ts - const matchSmallConfig = smallConfig.validator(); - type SmallConfig = typeof matchSmallConfig._TYPE; + const matchSmallInputSpec = smallInputSpec.validator(); + type SmallInputSpec = typeof matchSmallInputSpec._TYPE; ``` - Here is an example of a more complex configuration which came from a configuration for a service + Here is an example of a more complex input specification which came from an input specification for a service that works with bitcoin, like c-lightning. ```ts @@ -73,11 +73,11 @@ export const port = Value.number({ units: null, placeholder: null, }); -export const addNodesSpec = Config.of({ hostname: hostname, port: port }); +export const addNodesSpec = InputSpec.of({ hostname: hostname, port: port }); ``` */ -export class Config, Store = never> { +export class InputSpec, Store = never> { private constructor( private readonly spec: { [K in keyof Type]: Value | Value @@ -105,7 +105,7 @@ export class Config, Store = never> { validatorObj[key] = spec[key].validator } const validator = object(validatorObj) - return new Config< + return new InputSpec< { [K in keyof Spec]: Spec[K] extends | Value @@ -119,19 +119,19 @@ export class Config, Store = never> { /** * Use this during the times that the input needs a more specific type. - * Used in types that the value/ variant/ list/ config is constructed somewhere else. + * Used in types that the value/ variant/ list/ inputSpec is constructed somewhere else. ```ts - const a = Config.text({ + const a = InputSpec.text({ name: "a", required: false, }) - return Config.of()({ + return InputSpec.of()({ myValue: a.withStore(), }) ``` */ withStore() { - return this as any as Config + return this as any as InputSpec } } diff --git a/sdk/lib/config/builder/list.ts b/sdk/base/lib/actions/input/builder/list.ts similarity index 74% rename from sdk/lib/config/builder/list.ts rename to sdk/base/lib/actions/input/builder/list.ts index f230b8608..726dc961e 100644 --- a/sdk/lib/config/builder/list.ts +++ b/sdk/base/lib/actions/input/builder/list.ts @@ -1,4 +1,4 @@ -import { Config, LazyBuild } from "./config" +import { InputSpec, LazyBuild } from "./inputSpec" import { ListValueSpecText, Pattern, @@ -6,45 +6,55 @@ import { UniqueBy, ValueSpecList, ValueSpecListOf, -} from "../configTypes" -import { Parser, arrayOf, number, string } from "ts-matches" -/** - * Used as a subtype of Value.list -```ts -export const authorizationList = List.string({ - "name": "Authorization", - "range": "[0,*)", - "default": [], - "description": "Username and hashed password for JSON-RPC connections. RPC clients connect using the usual http basic authentication.", - "warning": null -}, {"masked":false,"placeholder":null,"pattern":"^[a-zA-Z0-9_-]+:([0-9a-fA-F]{2})+\\$([0-9a-fA-F]{2})+$","patternDescription":"Each item must be of the form \":$\"."}); -export const auth = Value.list(authorizationList); -``` -*/ +} from "../inputSpecTypes" +import { Parser, arrayOf, string } from "ts-matches" + export class List { private constructor( public build: LazyBuild, public validator: Parser, ) {} + static text( a: { name: string description?: string | null warning?: string | null - /** Default = [] */ default?: string[] minLength?: number | null maxLength?: number | null }, aSpec: { - /** Default = false */ + /** + * @description Mask (aka camouflage) text input with dots: ● ● ● + * @default false + */ masked?: boolean placeholder?: string | null minLength?: number | null maxLength?: number | null - patterns: Pattern[] - /** Default = "text" */ + /** + * @description A list of regular expressions to which the text must conform to pass validation. A human readable description is provided in case the validation fails. + * @default [] + * @example + * ``` + [ + { + regex: "[a-z]", + description: "May only contain lower case letters from the English alphabet." + } + ] + * ``` + */ + patterns?: Pattern[] + /** + * @description Informs the browser how to behave and which keyboard to display on mobile + * @default "text" + */ inputmode?: ListValueSpecText["inputmode"] + /** + * @description Displays a button that will generate a random string according to the provided charset and len attributes. + */ generate?: null | RandomString }, ) { @@ -57,6 +67,7 @@ export class List { masked: false, inputmode: "text" as const, generate: null, + patterns: aSpec.patterns || [], ...aSpec, } const built: ValueSpecListOf<"text"> = { @@ -73,6 +84,7 @@ export class List { return built }, arrayOf(string)) } + static dynamicText( getA: LazyBuild< Store, @@ -80,20 +92,17 @@ export class List { name: string description?: string | null warning?: string | null - /** Default = [] */ default?: string[] minLength?: number | null maxLength?: number | null disabled?: false | string generate?: null | RandomString spec: { - /** Default = false */ masked?: boolean placeholder?: string | null minLength?: number | null maxLength?: number | null - patterns: Pattern[] - /** Default = "text" */ + patterns?: Pattern[] inputmode?: ListValueSpecText["inputmode"] } } @@ -109,6 +118,7 @@ export class List { masked: false, inputmode: "text" as const, generate: null, + patterns: aSpec.patterns || [], ...aSpec, } const built: ValueSpecListOf<"text"> = { @@ -125,18 +135,18 @@ export class List { return built }, arrayOf(string)) } + static obj, Store>( a: { name: string description?: string | null warning?: string | null - /** Default [] */ default?: [] minLength?: number | null maxLength?: number | null }, aSpec: { - spec: Config + spec: InputSpec displayAs?: null | string uniqueBy?: null | UniqueBy }, @@ -170,14 +180,14 @@ export class List { /** * Use this during the times that the input needs a more specific type. - * Used in types that the value/ variant/ list/ config is constructed somewhere else. + * Used in types that the value/ variant/ list/ inputSpec is constructed somewhere else. ```ts - const a = Config.text({ + const a = InputSpec.text({ name: "a", required: false, }) - return Config.of()({ + return InputSpec.of()({ myValue: a.withStore(), }) ``` diff --git a/sdk/lib/config/builder/value.ts b/sdk/base/lib/actions/input/builder/value.ts similarity index 72% rename from sdk/lib/config/builder/value.ts rename to sdk/base/lib/actions/input/builder/value.ts index 01673a6df..ad30f1376 100644 --- a/sdk/lib/config/builder/value.ts +++ b/sdk/base/lib/actions/input/builder/value.ts @@ -1,4 +1,4 @@ -import { Config, LazyBuild, LazyBuildOptions } from "./config" +import { InputSpec, LazyBuild } from "./inputSpec" import { List } from "./list" import { Variants } from "./variants" import { @@ -7,13 +7,15 @@ import { RandomString, ValueSpec, ValueSpecDatetime, + ValueSpecHidden, ValueSpecText, ValueSpecTextarea, -} from "../configTypes" -import { DefaultString } from "../configTypes" -import { _ } from "../../util" +} from "../inputSpecTypes" +import { DefaultString } from "../inputSpecTypes" +import { _, once } from "../../../util" import { Parser, + any, anyOf, arrayOf, boolean, @@ -24,7 +26,6 @@ import { string, unknown, } from "ts-matches" -import { once } from "../../util/once" export type RequiredDefault = | false @@ -54,11 +55,6 @@ type AsRequired = MaybeRequiredType extends ? Type : Type | null | undefined -type InputAsRequired = A extends - | { required: { default: any } | never } - | never - ? Type - : Type | null | undefined const testForAsRequiredParser = once( () => object({ required: object({ default: unknown }) }).test, ) @@ -73,28 +69,6 @@ function asRequiredParser< return parser.optional() as any } -/** - * A value is going to be part of the form in the FE of the OS. - * Something like a boolean, a string, a number, etc. - * in the fe it will ask for the name of value, and use the rest of the value to determine how to render it. - * While writing with a value, you will start with `Value.` then let the IDE suggest the rest. - * for things like string, the options are going to be in {}. - * Keep an eye out for another config builder types as params. - * Note, usually this is going to be used in a `Config` {@link Config} builder. - ```ts -const username = Value.string({ - name: "Username", - default: "bitcoin", - description: "The username for connecting to Bitcoin over RPC.", - warning: null, - required: true, - masked: true, - placeholder: null, - pattern: "^[a-zA-Z0-9_]+$", - patternDescription: "Must be alphanumeric (can contain underscore).", -}); - ``` - */ export class Value { protected constructor( public build: LazyBuild, @@ -103,10 +77,13 @@ export class Value { static toggle(a: { name: string description?: string | null + /** Presents a warning prompt before permitting the value to change. */ warning?: string | null default: boolean - /** Immutable means it can only be configed at the first config then never again - Default is false */ + /** + * @description Once set, the value can never be changed. + * @default false + */ immutable?: boolean }) { return new Value( @@ -148,22 +125,53 @@ export class Value { static text>(a: { name: string description?: string | null + /** Presents a warning prompt before permitting the value to change. */ warning?: string | null + /** + * @description Determines if the field is required. If so, optionally provide a default value. + * @type { false | { default: string | RandomString | null } } + * @example required: false + * @example required: { default: null } + * @example required: { default: 'World' } + * @example required: { default: { charset: 'abcdefg', len: 16 } } + */ required: Required - - /** Default = false */ + /** + * @description Mask (aka camouflage) text input with dots: ● ● ● + * @default false + */ masked?: boolean placeholder?: string | null minLength?: number | null maxLength?: number | null + /** + * @description A list of regular expressions to which the text must conform to pass validation. A human readable description is provided in case the validation fails. + * @default [] + * @example + * ``` + [ + { + regex: "[a-z]", + description: "May only contain lower case letters from the English alphabet." + } + ] + * ``` + */ patterns?: Pattern[] - /** Default = 'text' */ + /** + * @description Informs the browser how to behave and which keyboard to display on mobile + * @default "text" + */ inputmode?: ValueSpecText["inputmode"] - /** Immutable means it can only be configured at the first config then never again - * Default is false + /** + * @description Once set, the value can never be changed. + * @default false */ immutable?: boolean - generate?: null | RandomString + /** + * @description Displays a button that will generate a random string according to the provided charset and len attributes. + */ + generate?: RandomString | null }) { return new Value, never>( async () => ({ @@ -193,19 +201,13 @@ export class Value { description?: string | null warning?: string | null required: RequiredDefault - - /** Default = false */ masked?: boolean placeholder?: string | null minLength?: number | null maxLength?: number | null patterns?: Pattern[] - /** Default = 'text' */ inputmode?: ValueSpecText["inputmode"] disabled?: string | false - /** Immutable means it can only be configured at the first config then never again - * Default is false - */ generate?: null | RandomString } >, @@ -233,13 +235,19 @@ export class Value { static textarea(a: { name: string description?: string | null + /** Presents a warning prompt before permitting the value to change. */ warning?: string | null + /** + * @description Unlike other "required" fields, for textarea this is a simple boolean. + */ required: boolean minLength?: number | null maxLength?: number | null placeholder?: string | null - /** Immutable means it can only be configed at the first config then never again - Default is false */ + /** + * @description Once set, the value can never be changed. + * @default false + */ immutable?: boolean }) { return new Value(async () => { @@ -290,17 +298,36 @@ export class Value { static number>(a: { name: string description?: string | null + /** Presents a warning prompt before permitting the value to change. */ warning?: string | null + /** + * @description Determines if the field is required. If so, optionally provide a default value. + * @type { false | { default: number | null } } + * @example required: false + * @example required: { default: null } + * @example required: { default: 7 } + */ required: Required min?: number | null max?: number | null - /** Default = '1' */ + /** + * @description How much does the number increase/decrease when using the arrows provided by the browser. + * @default 1 + */ step?: number | null + /** + * @description Requires the number to be an integer. + */ integer: boolean + /** + * @description Optionally display units to the right of the input box. + */ units?: string | null placeholder?: string | null - /** Immutable means it can only be configed at the first config then never again - Default is false */ + /** + * @description Once set, the value can never be changed. + * @default false + */ immutable?: boolean }) { return new Value, never>( @@ -331,7 +358,6 @@ export class Value { required: RequiredDefault min?: number | null max?: number | null - /** Default = '1' */ step?: number | null integer: boolean units?: string | null @@ -361,10 +387,20 @@ export class Value { static color>(a: { name: string description?: string | null + /** Presents a warning prompt before permitting the value to change. */ warning?: string | null + /** + * @description Determines if the field is required. If so, optionally provide a default value. + * @type { false | { default: string | null } } + * @example required: false + * @example required: { default: null } + * @example required: { default: 'ffffff' } + */ required: Required - /** Immutable means it can only be configed at the first config then never again - Default is false */ + /** + * @description Once set, the value can never be changed. + * @default false + */ immutable?: boolean }) { return new Value, never>( @@ -410,14 +446,27 @@ export class Value { static datetime>(a: { name: string description?: string | null + /** Presents a warning prompt before permitting the value to change. */ warning?: string | null + /** + * @description Determines if the field is required. If so, optionally provide a default value. + * @type { false | { default: string | null } } + * @example required: false + * @example required: { default: null } + * @example required: { default: '1985-12-16 18:00:00.000' } + */ required: Required - /** Default = 'datetime-local' */ + /** + * @description Informs the browser how to behave and which date/time component to display. + * @default "datetime-local" + */ inputmode?: ValueSpecDatetime["inputmode"] min?: string | null max?: string | null - /** Immutable means it can only be configed at the first config then never again - Default is false */ + /** + * @description Once set, the value can never be changed. + * @default false + */ immutable?: boolean }) { return new Value, never>( @@ -445,7 +494,6 @@ export class Value { description?: string | null warning?: string | null required: RequiredDefault - /** Default = 'datetime-local' */ inputmode?: ValueSpecDatetime["inputmode"] min?: string | null max?: string | null @@ -471,24 +519,39 @@ export class Value { } static select< Required extends RequiredDefault, - B extends Record, + Values extends Record, >(a: { name: string description?: string | null + /** Presents a warning prompt before permitting the value to change. */ warning?: string | null - required: Required - values: B /** - * Disabled: false means that there is nothing disabled, good to modify - * string means that this is the message displayed and the whole thing is disabled - * string[] means that the options are disabled + * @description Determines if the field is required. If so, optionally provide a default value from the list of values. + * @type { false | { default: string | null } } + * @example required: false + * @example required: { default: null } + * @example required: { default: 'radio1' } + */ + required: Required + /** + * @description A mapping of unique radio options to their human readable display format. + * @example + * ``` + { + radio1: "Radio 1" + radio2: "Radio 2" + radio3: "Radio 3" + } + * ``` + */ + values: Values + /** + * @description Once set, the value can never be changed. + * @default false */ - disabled?: false | string | (string & keyof B)[] - /** Immutable means it can only be configed at the first config then never again - Default is false */ immutable?: boolean }) { - return new Value, never>( + return new Value, never>( () => ({ description: null, warning: null, @@ -500,7 +563,9 @@ export class Value { }), asRequiredParser( anyOf( - ...Object.keys(a.values).map((x: keyof B & string) => literal(x)), + ...Object.keys(a.values).map((x: keyof Values & string) => + literal(x), + ), ), a, ) as any, @@ -515,11 +580,6 @@ export class Value { warning?: string | null required: RequiredDefault values: Record - /** - * Disabled: false means that there is nothing disabled, good to modify - * string means that this is the message displayed and the whole thing is disabled - * string[] means that the options are disabled - */ disabled?: false | string | string[] } >, @@ -540,20 +600,31 @@ export class Value { static multiselect>(a: { name: string description?: string | null + /** Presents a warning prompt before permitting the value to change. */ warning?: string | null + /** + * @description A simple list of which options should be checked by default. + */ default: string[] + /** + * @description A mapping of checkbox options to their human readable display format. + * @example + * ``` + { + option1: "Option 1" + option2: "Option 2" + option3: "Option 3" + } + * ``` + */ values: Values minLength?: number | null maxLength?: number | null - /** Immutable means it can only be configed at the first config then never again - Default is false */ - immutable?: boolean /** - * Disabled: false means that there is nothing disabled, good to modify - * string means that this is the message displayed and the whole thing is disabled - * string[] means that the options are disabled + * @description Once set, the value can never be changed. + * @default false */ - disabled?: false | string | (string & keyof Values)[] + immutable?: boolean }) { return new Value<(keyof Values)[], never>( () => ({ @@ -582,11 +653,6 @@ export class Value { values: Record minLength?: number | null maxLength?: number | null - /** - * Disabled: false means that there is nothing disabled, good to modify - * string means that this is the message displayed and the whole thing is disabled - * string[] means that the options are disabled - */ disabled?: false | string | string[] } >, @@ -609,9 +675,8 @@ export class Value { a: { name: string description?: string | null - warning?: string | null }, - spec: Config, + spec: InputSpec, ) { return new Value(async (options) => { const built = await spec.build(options as any) @@ -624,12 +689,11 @@ export class Value { } }, spec.validator) } - static file, Store>(a: { + static file(a: { name: string description?: string | null - warning?: string | null extensions: string[] - required: Required + required: boolean }) { const buildValue = { type: "file" as const, @@ -637,11 +701,9 @@ export class Value { warning: null, ...a, } - return new Value, Store>( + return new Value( () => ({ ...buildValue, - - ...requiredLikeToAbove(a.required), }), asRequiredParser(object({ filePath: string }), a), ) @@ -672,17 +734,21 @@ export class Value { a: { name: string description?: string | null + /** Presents a warning prompt before permitting the value to change. */ warning?: string | null - required: Required - /** Immutable means it can only be configed at the first config then never again - Default is false */ - immutable?: boolean /** - * Disabled: false means that there is nothing disabled, good to modify - * string means that this is the message displayed and the whole thing is disabled - * string[] means that the options are disabled + * @description Determines if the field is required. If so, optionally provide a default value from the list of variants. + * @type { false | { default: string | null } } + * @example required: false + * @example required: { default: null } + * @example required: { default: 'variant1' } */ - disabled?: false | string | string[] + required: Required + /** + * @description Once set, the value can never be changed. + * @default false + */ + immutable?: boolean }, aVariants: Variants, ) { @@ -736,11 +802,11 @@ export class Value { getA: LazyBuild< Store, { - disabled: string[] | false | string name: string description?: string | null warning?: string | null required: Required + disabled: string[] | false | string } >, aVariants: Variants | Variants, @@ -763,16 +829,25 @@ export class Value { return new Value((options) => a.build(options), a.validator) } + static hidden(parser: Parser = any) { + return new Value(async () => { + const built: ValueSpecHidden = { + type: "hidden" as const, + } + return built + }, parser) + } + /** * Use this during the times that the input needs a more specific type. - * Used in types that the value/ variant/ list/ config is constructed somewhere else. + * Used in types that the value/ variant/ list/ inputSpec is constructed somewhere else. ```ts - const a = Config.text({ + const a = InputSpec.text({ name: "a", required: false, }) - return Config.of()({ + return InputSpec.of()({ myValue: a.withStore(), }) ``` diff --git a/sdk/lib/config/builder/variants.ts b/sdk/base/lib/actions/input/builder/variants.ts similarity index 80% rename from sdk/lib/config/builder/variants.ts rename to sdk/base/lib/actions/input/builder/variants.ts index 352f16828..6c0f83905 100644 --- a/sdk/lib/config/builder/variants.ts +++ b/sdk/base/lib/actions/input/builder/variants.ts @@ -1,5 +1,5 @@ -import { InputSpec, ValueSpecUnion } from "../configTypes" -import { LazyBuild, Config } from "./config" +import { ValueSpec, ValueSpecUnion } from "../inputSpecTypes" +import { LazyBuild, InputSpec } from "./inputSpec" import { Parser, anyOf, literals, object } from "ts-matches" /** @@ -8,7 +8,7 @@ import { Parser, anyOf, literals, object } from "ts-matches" * key to the tag.id in the Value.select ```ts -export const disabled = Config.of({}); +export const disabled = InputSpec.of({}); export const size = Value.number({ name: "Max Chain Size", default: 550, @@ -20,7 +20,7 @@ export const size = Value.number({ units: "MiB", placeholder: null, }); -export const automatic = Config.of({ size: size }); +export const automatic = InputSpec.of({ size: size }); export const size1 = Value.number({ name: "Failsafe Chain Size", default: 65536, @@ -32,7 +32,7 @@ export const size1 = Value.number({ units: "MiB", placeholder: null, }); -export const manual = Config.of({ size: size1 }); +export const manual = InputSpec.of({ size: size1 }); export const pruningSettingsVariants = Variants.of({ disabled: { name: "Disabled", spec: disabled }, automatic: { name: "Automatic", spec: automatic }, @@ -61,7 +61,7 @@ export class Variants { VariantValues extends { [K in string]: { name: string - spec: Config | Config + spec: InputSpec | InputSpec } }, Store = never, @@ -81,14 +81,17 @@ export class Variants { selection: K // prettier-ignore value: - VariantValues[K]["spec"] extends (Config | Config) ? B : + VariantValues[K]["spec"] extends (InputSpec | InputSpec) ? B : never } }[keyof VariantValues], Store >(async (options) => { const variants = {} as { - [K in keyof VariantValues]: { name: string; spec: InputSpec } + [K in keyof VariantValues]: { + name: string + spec: Record + } } for (const key in a) { const value = a[key] @@ -102,14 +105,14 @@ export class Variants { } /** * Use this during the times that the input needs a more specific type. - * Used in types that the value/ variant/ list/ config is constructed somewhere else. + * Used in types that the value/ variant/ list/ inputSpec is constructed somewhere else. ```ts - const a = Config.text({ + const a = InputSpec.text({ name: "a", required: false, }) - return Config.of()({ + return InputSpec.of()({ myValue: a.withStore(), }) ``` diff --git a/sdk/base/lib/actions/input/index.ts b/sdk/base/lib/actions/input/index.ts new file mode 100644 index 000000000..3fc16f585 --- /dev/null +++ b/sdk/base/lib/actions/input/index.ts @@ -0,0 +1,3 @@ +export * as constants from "./inputSpecConstants" +export * as types from "./inputSpecTypes" +export * as builder from "./builder" diff --git a/sdk/lib/config/configConstants.ts b/sdk/base/lib/actions/input/inputSpecConstants.ts similarity index 72% rename from sdk/lib/config/configConstants.ts rename to sdk/base/lib/actions/input/inputSpecConstants.ts index aa0e024c9..3beaefd51 100644 --- a/sdk/lib/config/configConstants.ts +++ b/sdk/base/lib/actions/input/inputSpecConstants.ts @@ -1,14 +1,13 @@ -import { SmtpValue } from "../types" -import { GetSystemSmtp } from "../util/GetSystemSmtp" -import { email } from "../util/patterns" -import { Config, ConfigSpecOf } from "./builder/config" +import { SmtpValue } from "../../types" +import { GetSystemSmtp, Patterns } from "../../util" +import { InputSpec, InputSpecOf } from "./builder/inputSpec" import { Value } from "./builder/value" import { Variants } from "./builder/variants" /** * Base SMTP settings, to be used by StartOS for system wide SMTP */ -export const customSmtp = Config.of, never>({ +export const customSmtp = InputSpec.of, never>({ server: Value.text({ name: "SMTP Server", required: { @@ -29,7 +28,7 @@ export const customSmtp = Config.of, never>({ }, placeholder: "test@example.com", inputmode: "email", - patterns: [email], + patterns: [Patterns.email], }), login: Value.text({ name: "Login", @@ -45,9 +44,9 @@ export const customSmtp = Config.of, never>({ }) /** - * For service config. Gives users 3 options for SMTP: (1) disabled, (2) use system SMTP settings, (3) use custom SMTP settings + * For service inputSpec. Gives users 3 options for SMTP: (1) disabled, (2) use system SMTP settings, (3) use custom SMTP settings */ -export const smtpConfig = Value.filteredUnion( +export const smtpInputSpec = Value.filteredUnion( async ({ effects }) => { const smtp = await new GetSystemSmtp(effects).once() return smtp ? [] : ["system"] @@ -58,10 +57,10 @@ export const smtpConfig = Value.filteredUnion( required: { default: "disabled" }, }, Variants.of({ - disabled: { name: "Disabled", spec: Config.of({}) }, + disabled: { name: "Disabled", spec: InputSpec.of({}) }, system: { name: "System Credentials", - spec: Config.of({ + spec: InputSpec.of({ customFrom: Value.text({ name: "Custom From Address", description: @@ -69,7 +68,7 @@ export const smtpConfig = Value.filteredUnion( required: false, placeholder: "test@example.com", inputmode: "email", - patterns: [email], + patterns: [Patterns.email], }), }), }, diff --git a/sdk/lib/config/configTypes.ts b/sdk/base/lib/actions/input/inputSpecTypes.ts similarity index 74% rename from sdk/lib/config/configTypes.ts rename to sdk/base/lib/actions/input/inputSpecTypes.ts index 0179e531e..ee9189ae3 100644 --- a/sdk/lib/config/configTypes.ts +++ b/sdk/base/lib/actions/input/inputSpecTypes.ts @@ -12,6 +12,7 @@ export type ValueType = | "object" | "file" | "union" + | "hidden" export type ValueSpec = ValueSpecOf /** core spec types. These types provide the metadata for performing validations */ // prettier-ignore @@ -28,6 +29,7 @@ export type ValueSpecOf = T extends "object" ? ValueSpecObject : T extends "file" ? ValueSpecFile : T extends "union" ? ValueSpecUnion : + T extends "hidden" ? ValueSpecHidden : never export type ValueSpecText = { @@ -48,7 +50,6 @@ export type ValueSpecText = { default: DefaultString | null disabled: false | string generate: null | RandomString - /** Immutable means it can only be configured at the first config then never again */ immutable: boolean } export type ValueSpecTextarea = { @@ -62,7 +63,6 @@ export type ValueSpecTextarea = { maxLength: number | null required: boolean disabled: false | string - /** Immutable means it can only be configured at the first config then never again */ immutable: boolean } @@ -83,7 +83,6 @@ export type ValueSpecNumber = { required: boolean default: number | null disabled: false | string - /** Immutable means it can only be configured at the first config then never again */ immutable: boolean } export type ValueSpecColor = { @@ -95,7 +94,6 @@ export type ValueSpecColor = { required: boolean default: string | null disabled: false | string - /** Immutable means it can only be configured at the first config then never again */ immutable: boolean } export type ValueSpecDatetime = { @@ -109,7 +107,6 @@ export type ValueSpecDatetime = { max: string | null default: string | null disabled: false | string - /** Immutable means it can only be configured at the first config then never again */ immutable: boolean } export type ValueSpecSelect = { @@ -120,13 +117,7 @@ export type ValueSpecSelect = { type: "select" required: boolean default: string | null - /** - * Disabled: false means that there is nothing disabled, good to modify - * string means that this is the message displayed and the whole thing is disabled - * string[] means that the options are disabled - */ disabled: false | string | string[] - /** Immutable means it can only be configured at the first config then never again */ immutable: boolean } export type ValueSpecMultiselect = { @@ -139,14 +130,8 @@ export type ValueSpecMultiselect = { type: "multiselect" minLength: number | null maxLength: number | null - /** - * Disabled: false means that there is nothing disabled, good to modify - * string means that this is the message displayed and the whole thing is disabled - * string[] means that the options are disabled - */ disabled: false | string | string[] default: string[] - /** Immutable means it can only be configured at the first config then never again */ immutable: boolean } export type ValueSpecToggle = { @@ -157,7 +142,6 @@ export type ValueSpecToggle = { type: "toggle" default: boolean | null disabled: false | string - /** Immutable means it can only be configured at the first config then never again */ immutable: boolean } export type ValueSpecUnion = { @@ -173,15 +157,9 @@ export type ValueSpecUnion = { spec: InputSpec } > - /** - * Disabled: false means that there is nothing disabled, good to modify - * string means that this is the message displayed and the whole thing is disabled - * string[] means that the options are disabled - */ disabled: false | string | string[] required: boolean default: string | null - /** Immutable means it can only be configured at the first config then never again */ immutable: boolean } export type ValueSpecFile = { @@ -199,14 +177,15 @@ export type ValueSpecObject = { type: "object" spec: InputSpec } +export type ValueSpecHidden = { + type: "hidden" +} export type ListValueSpecType = "text" | "object" -/** represents a spec for the values of a list */ // prettier-ignore export type ListValueSpecOf = T extends "text" ? ListValueSpecText : T extends "object" ? ListValueSpecObject : never -/** represents a spec for a list */ export type ValueSpecList = ValueSpecListOf export type ValueSpecListOf = { name: string @@ -242,13 +221,13 @@ export type ListValueSpecText = { } export type ListValueSpecObject = { type: "object" - /** this is a mapped type of the config object at this level, replacing the object's values with specs on those values */ spec: InputSpec - /** indicates whether duplicates can be permitted in the list */ uniqueBy: UniqueBy - /** this should be a handlebars template which can make use of the entire config which corresponds to 'spec' */ displayAs: string | null } +// TODO Aiden do we really want this expressivity? Why not the below. Also what's with the "readonly" portion? +// export type UniqueBy = null | string | { any: string[] } | { all: string[] } + export type UniqueBy = | null | string diff --git a/sdk/base/lib/actions/setupActions.ts b/sdk/base/lib/actions/setupActions.ts new file mode 100644 index 000000000..f78da1f0a --- /dev/null +++ b/sdk/base/lib/actions/setupActions.ts @@ -0,0 +1,152 @@ +import { InputSpec } from "./input/builder" +import { ExtractInputSpecType } from "./input/builder/inputSpec" +import * as T from "../types" + +export type Run< + A extends + | Record + | InputSpec, any> + | InputSpec, never>, +> = (options: { + effects: T.Effects + input: ExtractInputSpecType & Record +}) => Promise +export type GetInput< + A extends + | Record + | InputSpec, any> + | InputSpec, never>, +> = (options: { + effects: T.Effects +}) => Promise & Record)> + +export type MaybeFn = T | ((options: { effects: T.Effects }) => Promise) +function callMaybeFn( + maybeFn: MaybeFn, + options: { effects: T.Effects }, +): Promise { + if (maybeFn instanceof Function) { + return maybeFn(options) + } else { + return Promise.resolve(maybeFn) + } +} +function mapMaybeFn( + maybeFn: MaybeFn, + map: (value: T) => U, +): MaybeFn { + if (maybeFn instanceof Function) { + return async (...args) => map(await maybeFn(...args)) + } else { + return map(maybeFn) + } +} + +export class Action< + Id extends T.ActionId, + Store, + InputSpecType extends + | Record + | InputSpec + | InputSpec, + Type extends + ExtractInputSpecType = ExtractInputSpecType, +> { + private constructor( + readonly id: Id, + private readonly metadataFn: MaybeFn, + private readonly inputSpec: InputSpecType, + private readonly getInputFn: GetInput, + private readonly runFn: Run, + ) {} + static withInput< + Id extends T.ActionId, + Store, + InputSpecType extends + | Record + | InputSpec + | InputSpec, + Type extends + ExtractInputSpecType = ExtractInputSpecType, + >( + id: Id, + metadata: MaybeFn>, + inputSpec: InputSpecType, + getInput: GetInput, + run: Run, + ): Action { + return new Action( + id, + mapMaybeFn(metadata, (m) => ({ ...m, hasInput: true })), + inputSpec, + getInput, + run, + ) + } + static withoutInput( + id: Id, + metadata: MaybeFn>, + run: Run<{}>, + ): Action { + return new Action( + id, + mapMaybeFn(metadata, (m) => ({ ...m, hasInput: true })), + {}, + async () => null, + run, + ) + } + async exportMetadata(options: { + effects: T.Effects + }): Promise { + const metadata = await callMaybeFn(this.metadataFn, options) + await options.effects.action.export({ id: this.id, metadata }) + return metadata + } + async getInput(options: { effects: T.Effects }): Promise { + return { + spec: await this.inputSpec.build(options), + value: (await this.getInputFn(options)) || null, + } + } + async run(options: { + effects: T.Effects + input: Type + }): Promise { + return this.runFn(options) + } +} + +export class Actions< + Store, + AllActions extends Record>, +> { + private constructor(private readonly actions: AllActions) {} + static of(): Actions { + return new Actions({}) + } + addAction>( + action: A, + ): Actions { + return new Actions({ ...this.actions, [action.id]: action }) + } + update(options: { effects: T.Effects }): Promise { + const updater = async (options: { effects: T.Effects }) => { + for (let action of Object.values(this.actions)) { + await action.exportMetadata(options) + } + await options.effects.action.clear({ except: Object.keys(this.actions) }) + } + const updaterCtx = { options } + updaterCtx.options = { + effects: { + ...options.effects, + constRetry: () => updater(updaterCtx.options), + }, + } + return updater(updaterCtx.options) + } + get(actionId: Id): AllActions[Id] { + return this.actions[actionId] + } +} diff --git a/sdk/base/lib/backup/Backups.ts b/sdk/base/lib/backup/Backups.ts new file mode 100644 index 000000000..3e644014a --- /dev/null +++ b/sdk/base/lib/backup/Backups.ts @@ -0,0 +1,208 @@ +import * as T from "../types" +import * as child_process from "child_process" +import { asError } from "../util" + +export const DEFAULT_OPTIONS: T.SyncOptions = { + delete: true, + exclude: [], +} +export type BackupSync = { + dataPath: `/media/startos/volumes/${Volumes}/${string}` + backupPath: `/media/startos/backup/${string}` + options?: Partial + backupOptions?: Partial + restoreOptions?: Partial +} +/** + * This utility simplifies the volume backup process. + * ```ts + * export const { createBackup, restoreBackup } = Backups.volumes("main").build(); + * ``` + * + * Changing the options of the rsync, (ie excludes) use either + * ```ts + * Backups.volumes("main").set_options({exclude: ['bigdata/']}).volumes('excludedVolume').build() + * // or + * Backups.with_options({exclude: ['bigdata/']}).volumes('excludedVolume').build() + * ``` + * + * Using the more fine control, using the addSets for more control + * ```ts + * Backups.addSets({ + * srcVolume: 'main', srcPath:'smallData/', dstPath: 'main/smallData/', dstVolume: : Backups.BACKUP + * }, { + * srcVolume: 'main', srcPath:'bigData/', dstPath: 'main/bigData/', dstVolume: : Backups.BACKUP, options: {exclude:['bigData/excludeThis']}} + * ).build()q + * ``` + */ +export class Backups { + private constructor( + private options = DEFAULT_OPTIONS, + private restoreOptions: Partial = {}, + private backupOptions: Partial = {}, + private backupSet = [] as BackupSync[], + ) {} + + static withVolumes( + ...volumeNames: Array + ): Backups { + return Backups.withSyncs( + ...volumeNames.map((srcVolume) => ({ + dataPath: `/media/startos/volumes/${srcVolume}/` as const, + backupPath: `/media/startos/backup/${srcVolume}/` as const, + })), + ) + } + + static withSyncs( + ...syncs: BackupSync[] + ) { + return syncs.reduce((acc, x) => acc.addSync(x), new Backups()) + } + + static withOptions( + options?: Partial, + ) { + return new Backups({ ...DEFAULT_OPTIONS, ...options }) + } + + setOptions(options?: Partial) { + this.options = { + ...this.options, + ...options, + } + return this + } + + setBackupOptions(options?: Partial) { + this.backupOptions = { + ...this.backupOptions, + ...options, + } + return this + } + + setRestoreOptions(options?: Partial) { + this.restoreOptions = { + ...this.restoreOptions, + ...options, + } + return this + } + + addVolume( + volume: M["volumes"][number], + options?: Partial<{ + options: T.SyncOptions + backupOptions: T.SyncOptions + restoreOptions: T.SyncOptions + }>, + ) { + return this.addSync({ + dataPath: `/media/startos/volumes/${volume}/` as const, + backupPath: `/media/startos/backup/${volume}/` as const, + ...options, + }) + } + addSync(sync: BackupSync) { + this.backupSet.push({ + ...sync, + options: { ...this.options, ...sync.options }, + }) + return this + } + + async createBackup() { + for (const item of this.backupSet) { + const rsyncResults = await runRsync({ + srcPath: item.dataPath, + dstPath: item.backupPath, + options: { + ...this.options, + ...this.backupOptions, + ...item.options, + ...item.backupOptions, + }, + }) + await rsyncResults.wait() + } + return + } + + async restoreBackup() { + for (const item of this.backupSet) { + const rsyncResults = await runRsync({ + srcPath: item.backupPath, + dstPath: item.dataPath, + options: { + ...this.options, + ...this.backupOptions, + ...item.options, + ...item.backupOptions, + }, + }) + await rsyncResults.wait() + } + return + } +} + +async function runRsync(rsyncOptions: { + srcPath: string + dstPath: string + options: T.SyncOptions +}): Promise<{ + id: () => Promise + wait: () => Promise + progress: () => Promise +}> { + const { srcPath, dstPath, options } = rsyncOptions + + const command = "rsync" + const args: string[] = [] + if (options.delete) { + args.push("--delete") + } + for (const exclude of options.exclude) { + args.push(`--exclude=${exclude}`) + } + args.push("-actAXH") + args.push("--info=progress2") + args.push("--no-inc-recursive") + args.push(srcPath) + args.push(dstPath) + const spawned = child_process.spawn(command, args, { detached: true }) + let percentage = 0.0 + spawned.stdout.on("data", (data: unknown) => { + const lines = String(data).replace("\r", "\n").split("\n") + for (const line of lines) { + const parsed = /$([0-9.]+)%/.exec(line)?.[1] + if (!parsed) continue + percentage = Number.parseFloat(parsed) + } + }) + + spawned.stderr.on("data", (data: unknown) => { + console.error(`Backups.runAsync`, asError(data)) + }) + + const id = async () => { + const pid = spawned.pid + if (pid === undefined) { + throw new Error("rsync process has no pid") + } + return String(pid) + } + const waitPromise = new Promise((resolve, reject) => { + spawned.on("exit", (code: any) => { + if (code === 0) { + resolve(null) + } else { + reject(new Error(`rsync exited with code ${code}`)) + } + }) + }) + const wait = () => waitPromise + const progress = () => Promise.resolve(percentage) + return { id, wait, progress } +} diff --git a/sdk/base/lib/backup/setupBackups.ts b/sdk/base/lib/backup/setupBackups.ts new file mode 100644 index 000000000..b41a61f42 --- /dev/null +++ b/sdk/base/lib/backup/setupBackups.ts @@ -0,0 +1,39 @@ +import { Backups } from "./Backups" +import * as T from "../types" +import { _ } from "../util" + +export type SetupBackupsParams = + | M["volumes"][number][] + | ((_: { effects: T.Effects }) => Promise>) + +type SetupBackupsRes = { + createBackup: T.ExpectedExports.createBackup + restoreBackup: T.ExpectedExports.restoreBackup +} + +export function setupBackups( + options: SetupBackupsParams, +) { + let backupsFactory: (_: { effects: T.Effects }) => Promise> + if (options instanceof Function) { + backupsFactory = options + } else { + backupsFactory = async () => Backups.withVolumes(...options) + } + const answer: { + createBackup: T.ExpectedExports.createBackup + restoreBackup: T.ExpectedExports.restoreBackup + } = { + get createBackup() { + return (async (options) => { + return (await backupsFactory(options)).createBackup() + }) as T.ExpectedExports.createBackup + }, + get restoreBackup() { + return (async (options) => { + return (await backupsFactory(options)).restoreBackup() + }) as T.ExpectedExports.restoreBackup + }, + } + return answer +} diff --git a/sdk/base/lib/dependencies/Dependency.ts b/sdk/base/lib/dependencies/Dependency.ts new file mode 100644 index 000000000..8a1a862c7 --- /dev/null +++ b/sdk/base/lib/dependencies/Dependency.ts @@ -0,0 +1,21 @@ +import { VersionRange } from "../exver" + +export class Dependency { + constructor( + readonly data: + | { + /** Either "running" or "exists". Does the dependency need to be running, or does it only need to exist? */ + type: "running" + /** The acceptable version range of the dependency. */ + versionRange: VersionRange + /** A list of the dependency's health check IDs that must be passing for the service to be satisfied. */ + healthChecks: string[] + } + | { + /** Either "running" or "exists". Does the dependency need to be running, or does it only need to exist? */ + type: "exists" + /** The acceptable version range of the dependency. */ + versionRange: VersionRange + }, + ) {} +} diff --git a/sdk/lib/dependencies/dependencies.ts b/sdk/base/lib/dependencies/dependencies.ts similarity index 89% rename from sdk/lib/dependencies/dependencies.ts rename to sdk/base/lib/dependencies/dependencies.ts index 287f63b06..fd3c6bb69 100644 --- a/sdk/lib/dependencies/dependencies.ts +++ b/sdk/base/lib/dependencies/dependencies.ts @@ -1,18 +1,12 @@ import { ExtendedVersion, VersionRange } from "../exver" -import { - Effects, - PackageId, - DependencyRequirement, - SetHealth, - CheckDependenciesResult, - HealthCheckId, -} from "../types" +import { PackageId, HealthCheckId } from "../types" +import { Effects } from "../Effects" export type CheckDependencies = { installedSatisfied: (packageId: DependencyId) => boolean installedVersionSatisfied: (packageId: DependencyId) => boolean runningSatisfied: (packageId: DependencyId) => boolean - configSatisfied: (packageId: DependencyId) => boolean + actionsSatisfied: (packageId: DependencyId) => boolean healthCheckSatisfied: ( packageId: DependencyId, healthCheckId: HealthCheckId, @@ -22,7 +16,7 @@ export type CheckDependencies = { throwIfInstalledNotSatisfied: (packageId: DependencyId) => void throwIfInstalledVersionNotSatisfied: (packageId: DependencyId) => void throwIfRunningNotSatisfied: (packageId: DependencyId) => void - throwIfConfigNotSatisfied: (packageId: DependencyId) => void + throwIfActionsNotSatisfied: (packageId: DependencyId) => void throwIfHealthNotSatisfied: ( packageId: DependencyId, healthCheckId?: HealthCheckId, @@ -71,8 +65,8 @@ export async function checkDependencies< const dep = find(packageId) return dep.requirement.kind !== "running" || dep.result.isRunning } - const configSatisfied = (packageId: DependencyId) => - find(packageId).result.configSatisfied + const actionsSatisfied = (packageId: DependencyId) => + Object.keys(find(packageId).result.requestedActions).length === 0 const healthCheckSatisfied = ( packageId: DependencyId, healthCheckId?: HealthCheckId, @@ -94,7 +88,7 @@ export async function checkDependencies< installedSatisfied(packageId) && installedVersionSatisfied(packageId) && runningSatisfied(packageId) && - configSatisfied(packageId) && + actionsSatisfied(packageId) && healthCheckSatisfied(packageId) const satisfied = (packageId?: DependencyId) => packageId @@ -130,11 +124,12 @@ export async function checkDependencies< throw new Error(`${dep.result.title || packageId} is not running`) } } - const throwIfConfigNotSatisfied = (packageId: DependencyId) => { + const throwIfActionsNotSatisfied = (packageId: DependencyId) => { const dep = find(packageId) - if (!dep.result.configSatisfied) { + const reqs = Object.keys(dep.result.requestedActions) + if (reqs.length) { throw new Error( - `${dep.result.title || packageId}'s configuration does not satisfy requirements`, + `The following action requests have not been fulfilled: ${reqs.join(", ")}`, ) } } @@ -168,7 +163,7 @@ export async function checkDependencies< throwIfInstalledNotSatisfied(packageId) throwIfInstalledVersionNotSatisfied(packageId) throwIfRunningNotSatisfied(packageId) - throwIfConfigNotSatisfied(packageId) + throwIfActionsNotSatisfied(packageId) throwIfHealthNotSatisfied(packageId) } const throwIfNotSatisfied = (packageId?: DependencyId) => @@ -193,13 +188,13 @@ export async function checkDependencies< installedSatisfied, installedVersionSatisfied, runningSatisfied, - configSatisfied, + actionsSatisfied, healthCheckSatisfied, satisfied, throwIfInstalledNotSatisfied, throwIfInstalledVersionNotSatisfied, throwIfRunningNotSatisfied, - throwIfConfigNotSatisfied, + throwIfActionsNotSatisfied, throwIfHealthNotSatisfied, throwIfNotSatisfied, } diff --git a/sdk/lib/dependencies/index.ts b/sdk/base/lib/dependencies/index.ts similarity index 78% rename from sdk/lib/dependencies/index.ts rename to sdk/base/lib/dependencies/index.ts index 3fe78b4f3..09e2b33ad 100644 --- a/sdk/lib/dependencies/index.ts +++ b/sdk/base/lib/dependencies/index.ts @@ -4,6 +4,3 @@ export type ReadonlyDeep = A extends {} ? { readonly [K in keyof A]: ReadonlyDeep } : A; export type MaybePromise = Promise | A export type Message = string - -import "./DependencyConfig" -import "./setupDependencyConfig" diff --git a/sdk/base/lib/dependencies/setupDependencies.ts b/sdk/base/lib/dependencies/setupDependencies.ts new file mode 100644 index 000000000..0f9bd6da8 --- /dev/null +++ b/sdk/base/lib/dependencies/setupDependencies.ts @@ -0,0 +1,56 @@ +import * as T from "../types" +import { Dependency } from "./Dependency" + +type DependencyType = { + [K in keyof { + [K in keyof Manifest["dependencies"]]: Manifest["dependencies"][K]["optional"] extends false + ? K + : never + }]: Dependency +} & { + [K in keyof { + [K in keyof Manifest["dependencies"]]: Manifest["dependencies"][K]["optional"] extends true + ? K + : never + }]?: Dependency +} + +export function setupDependencies( + fn: (options: { effects: T.Effects }) => Promise>, +): (options: { effects: T.Effects }) => Promise { + return (options: { effects: T.Effects }) => { + const updater = async (options: { effects: T.Effects }) => { + const dependencyType = await fn(options) + return await options.effects.setDependencies({ + dependencies: Object.entries(dependencyType).map( + ([ + id, + { + data: { versionRange, ...x }, + }, + ]) => ({ + id, + ...x, + ...(x.type === "running" + ? { + kind: "running", + healthChecks: x.healthChecks, + } + : { + kind: "exists", + }), + versionRange: versionRange.toString(), + }), + ), + }) + } + const updaterCtx = { options } + updaterCtx.options = { + effects: { + ...options.effects, + constRetry: () => updater(updaterCtx.options), + }, + } + return updater(updaterCtx.options) + } +} diff --git a/sdk/lib/exver/exver.pegjs b/sdk/base/lib/exver/exver.pegjs similarity index 100% rename from sdk/lib/exver/exver.pegjs rename to sdk/base/lib/exver/exver.pegjs diff --git a/sdk/lib/exver/exver.ts b/sdk/base/lib/exver/exver.ts similarity index 100% rename from sdk/lib/exver/exver.ts rename to sdk/base/lib/exver/exver.ts diff --git a/sdk/lib/exver/index.ts b/sdk/base/lib/exver/index.ts similarity index 100% rename from sdk/lib/exver/index.ts rename to sdk/base/lib/exver/index.ts diff --git a/sdk/lib/index.browser.ts b/sdk/base/lib/index.ts similarity index 51% rename from sdk/lib/index.browser.ts rename to sdk/base/lib/index.ts index f7d645133..0aa8e4758 100644 --- a/sdk/lib/index.browser.ts +++ b/sdk/base/lib/index.ts @@ -1,13 +1,12 @@ export { S9pk } from "./s9pk" export { VersionRange, ExtendedVersion, Version } from "./exver" -export * as config from "./config" -export * as CB from "./config/builder" -export * as CT from "./config/configTypes" -export * as dependencyConfig from "./dependencies" +export * as inputSpec from "./actions/input" +export * as ISB from "./actions/input/builder" +export * as IST from "./actions/input/inputSpecTypes" export * as types from "./types" export * as T from "./types" export * as yaml from "yaml" export * as matches from "ts-matches" -export * as utils from "./util/index.browser" +export * as utils from "./util" diff --git a/sdk/lib/interfaces/AddressReceipt.ts b/sdk/base/lib/interfaces/AddressReceipt.ts similarity index 100% rename from sdk/lib/interfaces/AddressReceipt.ts rename to sdk/base/lib/interfaces/AddressReceipt.ts diff --git a/sdk/lib/interfaces/Host.ts b/sdk/base/lib/interfaces/Host.ts similarity index 82% rename from sdk/lib/interfaces/Host.ts rename to sdk/base/lib/interfaces/Host.ts index aa27a289c..b90dc1c60 100644 --- a/sdk/lib/interfaces/Host.ts +++ b/sdk/base/lib/interfaces/Host.ts @@ -1,10 +1,10 @@ -import { number, object, string } from "ts-matches" -import { Effects } from "../types" +import { object, string } from "ts-matches" +import { Effects } from "../Effects" import { Origin } from "./Origin" -import { AddSslOptions, BindParams } from ".././osBindings" -import { Security } from ".././osBindings" -import { BindOptions } from ".././osBindings" -import { AlpnInfo } from ".././osBindings" +import { AddSslOptions, BindParams } from "../osBindings" +import { Security } from "../osBindings" +import { BindOptions } from "../osBindings" +import { AlpnInfo } from "../osBindings" export { AddSslOptions, Security, BindOptions } @@ -94,6 +94,22 @@ export class Host { }, ) {} + /** + * @description Use this function to bind the host to an internal port and configured options for protocol, security, and external port. + * + * @param internalPort - The internal port to be bound. + * @param options - The protocol options for this binding. + * @returns A multi-origin that is capable of exporting one or more service interfaces. + * @example + * In this example, we bind a previously created multi-host to port 80, then select the http protocol and request an external port of 8332. + * + * ``` + const uiMultiOrigin = await uiMulti.bindPort(80, { + protocol: 'http', + preferredExternalPort: 8332, + }) + * ``` + */ async bindPort( internalPort: number, options: BindOptionsByProtocol, diff --git a/sdk/lib/interfaces/Origin.ts b/sdk/base/lib/interfaces/Origin.ts similarity index 89% rename from sdk/lib/interfaces/Origin.ts rename to sdk/base/lib/interfaces/Origin.ts index cc84728ec..5e12713e6 100644 --- a/sdk/lib/interfaces/Origin.ts +++ b/sdk/base/lib/interfaces/Origin.ts @@ -1,6 +1,6 @@ import { AddressInfo } from "../types" import { AddressReceipt } from "./AddressReceipt" -import { Host, BindOptions, Scheme } from "./Host" +import { Host, Scheme } from "./Host" import { ServiceInterfaceBuilder } from "./ServiceInterfaceBuilder" export class Origin { @@ -31,9 +31,9 @@ export class Origin { } /** - * A function to register a group of origins ( :// : ) with StartOS + * @description A function to register a group of origins ( :// : ) with StartOS * - * The returned addressReceipt serves as proof that the addresses were registered + * The returned addressReceipt serves as proof that the addresses were registered * * @param addressInfo * @returns diff --git a/sdk/lib/interfaces/ServiceInterfaceBuilder.ts b/sdk/base/lib/interfaces/ServiceInterfaceBuilder.ts similarity index 90% rename from sdk/lib/interfaces/ServiceInterfaceBuilder.ts rename to sdk/base/lib/interfaces/ServiceInterfaceBuilder.ts index 49d8020d6..4ef294b4f 100644 --- a/sdk/lib/interfaces/ServiceInterfaceBuilder.ts +++ b/sdk/base/lib/interfaces/ServiceInterfaceBuilder.ts @@ -1,5 +1,5 @@ -import { ServiceInterfaceType } from "../StartSdk" -import { Effects } from "../types" +import { ServiceInterfaceType } from "../types" +import { Effects } from "../Effects" import { Scheme } from "./Host" /** diff --git a/sdk/lib/interfaces/interfaceReceipt.ts b/sdk/base/lib/interfaces/interfaceReceipt.ts similarity index 100% rename from sdk/lib/interfaces/interfaceReceipt.ts rename to sdk/base/lib/interfaces/interfaceReceipt.ts diff --git a/sdk/base/lib/interfaces/setupInterfaces.ts b/sdk/base/lib/interfaces/setupInterfaces.ts new file mode 100644 index 000000000..474841aa3 --- /dev/null +++ b/sdk/base/lib/interfaces/setupInterfaces.ts @@ -0,0 +1,54 @@ +import * as T from "../types" +import { AddressReceipt } from "./AddressReceipt" + +declare const UpdateServiceInterfacesProof: unique symbol +export type UpdateServiceInterfacesReceipt = { + [UpdateServiceInterfacesProof]: never +} + +export type ServiceInterfacesReceipt = Array +export type SetServiceInterfaces = + (opts: { effects: T.Effects }) => Promise +export type UpdateServiceInterfaces = + (opts: { + effects: T.Effects + }) => Promise +export type SetupServiceInterfaces = ( + fn: SetServiceInterfaces, +) => UpdateServiceInterfaces +export const NO_INTERFACE_CHANGES = {} as UpdateServiceInterfacesReceipt +export const setupServiceInterfaces: SetupServiceInterfaces = < + Output extends ServiceInterfacesReceipt, +>( + fn: SetServiceInterfaces, +) => + ((options: { effects: T.Effects }) => { + const updater = async (options: { effects: T.Effects }) => { + const bindings: T.BindId[] = [] + const interfaces: T.ServiceInterfaceId[] = [] + const res = await fn({ + effects: { + ...options.effects, + bind: (params: T.BindParams) => { + bindings.push({ id: params.id, internalPort: params.internalPort }) + return options.effects.bind(params) + }, + exportServiceInterface: (params: T.ExportServiceInterfaceParams) => { + interfaces.push(params.id) + return options.effects.exportServiceInterface(params) + }, + }, + }) + await options.effects.clearBindings({ except: bindings }) + await options.effects.clearServiceInterfaces({ except: interfaces }) + return res + } + const updaterCtx = { options } + updaterCtx.options = { + effects: { + ...options.effects, + constRetry: () => updater(updaterCtx.options), + }, + } + return updater(updaterCtx.options) + }) as UpdateServiceInterfaces diff --git a/sdk/lib/osBindings/AcceptSigners.ts b/sdk/base/lib/osBindings/AcceptSigners.ts similarity index 100% rename from sdk/lib/osBindings/AcceptSigners.ts rename to sdk/base/lib/osBindings/AcceptSigners.ts diff --git a/sdk/lib/osBindings/ActionId.ts b/sdk/base/lib/osBindings/ActionId.ts similarity index 100% rename from sdk/lib/osBindings/ActionId.ts rename to sdk/base/lib/osBindings/ActionId.ts diff --git a/sdk/lib/osBindings/Status.ts b/sdk/base/lib/osBindings/ActionInput.ts similarity index 50% rename from sdk/lib/osBindings/Status.ts rename to sdk/base/lib/osBindings/ActionInput.ts index b784f4d6a..a19a5f1a4 100644 --- a/sdk/lib/osBindings/Status.ts +++ b/sdk/base/lib/osBindings/ActionInput.ts @@ -1,4 +1,6 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { MainStatus } from "./MainStatus" -export type Status = { configured: boolean; main: MainStatus } +export type ActionInput = { + spec: Record + value: Record | null +} diff --git a/sdk/lib/osBindings/ActionMetadata.ts b/sdk/base/lib/osBindings/ActionMetadata.ts similarity index 74% rename from sdk/lib/osBindings/ActionMetadata.ts rename to sdk/base/lib/osBindings/ActionMetadata.ts index b103b82b0..ade129fd4 100644 --- a/sdk/lib/osBindings/ActionMetadata.ts +++ b/sdk/base/lib/osBindings/ActionMetadata.ts @@ -1,12 +1,13 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ActionVisibility } from "./ActionVisibility" import type { AllowedStatuses } from "./AllowedStatuses" export type ActionMetadata = { name: string description: string warning: string | null - input: any - disabled: boolean + visibility: ActionVisibility allowedStatuses: AllowedStatuses + hasInput: boolean group: string | null } diff --git a/sdk/base/lib/osBindings/ActionRequest.ts b/sdk/base/lib/osBindings/ActionRequest.ts new file mode 100644 index 000000000..63b5607d8 --- /dev/null +++ b/sdk/base/lib/osBindings/ActionRequest.ts @@ -0,0 +1,13 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ActionId } from "./ActionId" +import type { ActionRequestInput } from "./ActionRequestInput" +import type { ActionRequestTrigger } from "./ActionRequestTrigger" +import type { PackageId } from "./PackageId" + +export type ActionRequest = { + packageId: PackageId + actionId: ActionId + description?: string + when?: ActionRequestTrigger + input?: ActionRequestInput +} diff --git a/sdk/lib/osBindings/AllowedStatuses.ts b/sdk/base/lib/osBindings/ActionRequestCondition.ts similarity index 61% rename from sdk/lib/osBindings/AllowedStatuses.ts rename to sdk/base/lib/osBindings/ActionRequestCondition.ts index 960187fd9..0f06caf3c 100644 --- a/sdk/lib/osBindings/AllowedStatuses.ts +++ b/sdk/base/lib/osBindings/ActionRequestCondition.ts @@ -1,3 +1,3 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type AllowedStatuses = "onlyRunning" | "onlyStopped" | "any" +export type ActionRequestCondition = "input-not-matches" diff --git a/sdk/base/lib/osBindings/ActionRequestEntry.ts b/sdk/base/lib/osBindings/ActionRequestEntry.ts new file mode 100644 index 000000000..0e716abe4 --- /dev/null +++ b/sdk/base/lib/osBindings/ActionRequestEntry.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ActionRequest } from "./ActionRequest" + +export type ActionRequestEntry = { request: ActionRequest; active: boolean } diff --git a/sdk/base/lib/osBindings/ActionRequestInput.ts b/sdk/base/lib/osBindings/ActionRequestInput.ts new file mode 100644 index 000000000..a1cde7789 --- /dev/null +++ b/sdk/base/lib/osBindings/ActionRequestInput.ts @@ -0,0 +1,6 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ActionRequestInput = { + kind: "partial" + value: Record +} diff --git a/sdk/base/lib/osBindings/ActionRequestTrigger.ts b/sdk/base/lib/osBindings/ActionRequestTrigger.ts new file mode 100644 index 000000000..ebd0963e5 --- /dev/null +++ b/sdk/base/lib/osBindings/ActionRequestTrigger.ts @@ -0,0 +1,7 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ActionRequestCondition } from "./ActionRequestCondition" + +export type ActionRequestTrigger = { + once: boolean + condition: ActionRequestCondition +} diff --git a/sdk/base/lib/osBindings/ActionVisibility.ts b/sdk/base/lib/osBindings/ActionVisibility.ts new file mode 100644 index 000000000..f7d5a2a2a --- /dev/null +++ b/sdk/base/lib/osBindings/ActionVisibility.ts @@ -0,0 +1,6 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ActionVisibility = + | "hidden" + | { disabled: { reason: string } } + | "enabled" diff --git a/sdk/lib/osBindings/AddAdminParams.ts b/sdk/base/lib/osBindings/AddAdminParams.ts similarity index 100% rename from sdk/lib/osBindings/AddAdminParams.ts rename to sdk/base/lib/osBindings/AddAdminParams.ts diff --git a/sdk/lib/osBindings/AddAssetParams.ts b/sdk/base/lib/osBindings/AddAssetParams.ts similarity index 100% rename from sdk/lib/osBindings/AddAssetParams.ts rename to sdk/base/lib/osBindings/AddAssetParams.ts diff --git a/sdk/lib/osBindings/AddPackageParams.ts b/sdk/base/lib/osBindings/AddPackageParams.ts similarity index 100% rename from sdk/lib/osBindings/AddPackageParams.ts rename to sdk/base/lib/osBindings/AddPackageParams.ts diff --git a/sdk/lib/osBindings/AddSslOptions.ts b/sdk/base/lib/osBindings/AddSslOptions.ts similarity index 100% rename from sdk/lib/osBindings/AddSslOptions.ts rename to sdk/base/lib/osBindings/AddSslOptions.ts diff --git a/sdk/lib/osBindings/AddVersionParams.ts b/sdk/base/lib/osBindings/AddVersionParams.ts similarity index 100% rename from sdk/lib/osBindings/AddVersionParams.ts rename to sdk/base/lib/osBindings/AddVersionParams.ts diff --git a/sdk/lib/osBindings/AddressInfo.ts b/sdk/base/lib/osBindings/AddressInfo.ts similarity index 100% rename from sdk/lib/osBindings/AddressInfo.ts rename to sdk/base/lib/osBindings/AddressInfo.ts diff --git a/sdk/lib/osBindings/Alerts.ts b/sdk/base/lib/osBindings/Alerts.ts similarity index 100% rename from sdk/lib/osBindings/Alerts.ts rename to sdk/base/lib/osBindings/Alerts.ts diff --git a/sdk/lib/osBindings/Algorithm.ts b/sdk/base/lib/osBindings/Algorithm.ts similarity index 100% rename from sdk/lib/osBindings/Algorithm.ts rename to sdk/base/lib/osBindings/Algorithm.ts diff --git a/sdk/lib/osBindings/AllPackageData.ts b/sdk/base/lib/osBindings/AllPackageData.ts similarity index 100% rename from sdk/lib/osBindings/AllPackageData.ts rename to sdk/base/lib/osBindings/AllPackageData.ts diff --git a/sdk/base/lib/osBindings/AllowedStatuses.ts b/sdk/base/lib/osBindings/AllowedStatuses.ts new file mode 100644 index 000000000..ed5851495 --- /dev/null +++ b/sdk/base/lib/osBindings/AllowedStatuses.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type AllowedStatuses = "only-running" | "only-stopped" | "any" diff --git a/sdk/lib/osBindings/AlpnInfo.ts b/sdk/base/lib/osBindings/AlpnInfo.ts similarity index 100% rename from sdk/lib/osBindings/AlpnInfo.ts rename to sdk/base/lib/osBindings/AlpnInfo.ts diff --git a/sdk/lib/osBindings/AnySignature.ts b/sdk/base/lib/osBindings/AnySignature.ts similarity index 100% rename from sdk/lib/osBindings/AnySignature.ts rename to sdk/base/lib/osBindings/AnySignature.ts diff --git a/sdk/lib/osBindings/AnySigningKey.ts b/sdk/base/lib/osBindings/AnySigningKey.ts similarity index 100% rename from sdk/lib/osBindings/AnySigningKey.ts rename to sdk/base/lib/osBindings/AnySigningKey.ts diff --git a/sdk/lib/osBindings/AnyVerifyingKey.ts b/sdk/base/lib/osBindings/AnyVerifyingKey.ts similarity index 100% rename from sdk/lib/osBindings/AnyVerifyingKey.ts rename to sdk/base/lib/osBindings/AnyVerifyingKey.ts diff --git a/sdk/lib/osBindings/ApiState.ts b/sdk/base/lib/osBindings/ApiState.ts similarity index 100% rename from sdk/lib/osBindings/ApiState.ts rename to sdk/base/lib/osBindings/ApiState.ts diff --git a/sdk/lib/osBindings/AttachParams.ts b/sdk/base/lib/osBindings/AttachParams.ts similarity index 100% rename from sdk/lib/osBindings/AttachParams.ts rename to sdk/base/lib/osBindings/AttachParams.ts diff --git a/sdk/lib/osBindings/BackupProgress.ts b/sdk/base/lib/osBindings/BackupProgress.ts similarity index 100% rename from sdk/lib/osBindings/BackupProgress.ts rename to sdk/base/lib/osBindings/BackupProgress.ts diff --git a/sdk/lib/osBindings/BackupTargetFS.ts b/sdk/base/lib/osBindings/BackupTargetFS.ts similarity index 100% rename from sdk/lib/osBindings/BackupTargetFS.ts rename to sdk/base/lib/osBindings/BackupTargetFS.ts diff --git a/sdk/lib/osBindings/Base64.ts b/sdk/base/lib/osBindings/Base64.ts similarity index 100% rename from sdk/lib/osBindings/Base64.ts rename to sdk/base/lib/osBindings/Base64.ts diff --git a/sdk/base/lib/osBindings/BindId.ts b/sdk/base/lib/osBindings/BindId.ts new file mode 100644 index 000000000..778d95346 --- /dev/null +++ b/sdk/base/lib/osBindings/BindId.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { HostId } from "./HostId" + +export type BindId = { id: HostId; internalPort: number } diff --git a/sdk/lib/osBindings/BindInfo.ts b/sdk/base/lib/osBindings/BindInfo.ts similarity index 71% rename from sdk/lib/osBindings/BindInfo.ts rename to sdk/base/lib/osBindings/BindInfo.ts index 221b1c37c..85fc38e94 100644 --- a/sdk/lib/osBindings/BindInfo.ts +++ b/sdk/base/lib/osBindings/BindInfo.ts @@ -2,4 +2,4 @@ import type { BindOptions } from "./BindOptions" import type { LanInfo } from "./LanInfo" -export type BindInfo = { options: BindOptions; lan: LanInfo } +export type BindInfo = { enabled: boolean; options: BindOptions; lan: LanInfo } diff --git a/sdk/lib/osBindings/BindOptions.ts b/sdk/base/lib/osBindings/BindOptions.ts similarity index 100% rename from sdk/lib/osBindings/BindOptions.ts rename to sdk/base/lib/osBindings/BindOptions.ts diff --git a/sdk/lib/osBindings/BindParams.ts b/sdk/base/lib/osBindings/BindParams.ts similarity index 100% rename from sdk/lib/osBindings/BindParams.ts rename to sdk/base/lib/osBindings/BindParams.ts diff --git a/sdk/lib/osBindings/Blake3Commitment.ts b/sdk/base/lib/osBindings/Blake3Commitment.ts similarity index 100% rename from sdk/lib/osBindings/Blake3Commitment.ts rename to sdk/base/lib/osBindings/Blake3Commitment.ts diff --git a/sdk/lib/osBindings/BlockDev.ts b/sdk/base/lib/osBindings/BlockDev.ts similarity index 100% rename from sdk/lib/osBindings/BlockDev.ts rename to sdk/base/lib/osBindings/BlockDev.ts diff --git a/sdk/lib/osBindings/CallbackId.ts b/sdk/base/lib/osBindings/CallbackId.ts similarity index 100% rename from sdk/lib/osBindings/CallbackId.ts rename to sdk/base/lib/osBindings/CallbackId.ts diff --git a/sdk/lib/osBindings/Category.ts b/sdk/base/lib/osBindings/Category.ts similarity index 100% rename from sdk/lib/osBindings/Category.ts rename to sdk/base/lib/osBindings/Category.ts diff --git a/sdk/lib/osBindings/CheckDependenciesParam.ts b/sdk/base/lib/osBindings/CheckDependenciesParam.ts similarity index 100% rename from sdk/lib/osBindings/CheckDependenciesParam.ts rename to sdk/base/lib/osBindings/CheckDependenciesParam.ts diff --git a/sdk/lib/osBindings/CheckDependenciesResult.ts b/sdk/base/lib/osBindings/CheckDependenciesResult.ts similarity index 62% rename from sdk/lib/osBindings/CheckDependenciesResult.ts rename to sdk/base/lib/osBindings/CheckDependenciesResult.ts index a435ff87f..3fa34b600 100644 --- a/sdk/lib/osBindings/CheckDependenciesResult.ts +++ b/sdk/base/lib/osBindings/CheckDependenciesResult.ts @@ -1,14 +1,17 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ActionRequestEntry } from "./ActionRequestEntry" import type { HealthCheckId } from "./HealthCheckId" import type { NamedHealthCheckResult } from "./NamedHealthCheckResult" import type { PackageId } from "./PackageId" +import type { ReplayId } from "./ReplayId" +import type { Version } from "./Version" export type CheckDependenciesResult = { packageId: PackageId title: string | null - installedVersion: string | null - satisfies: string[] + installedVersion: Version | null + satisfies: Array isRunning: boolean - configSatisfied: boolean + requestedActions: { [key: ReplayId]: ActionRequestEntry } healthChecks: { [key: HealthCheckId]: NamedHealthCheckResult } } diff --git a/sdk/lib/osBindings/Cifs.ts b/sdk/base/lib/osBindings/Cifs.ts similarity index 100% rename from sdk/lib/osBindings/Cifs.ts rename to sdk/base/lib/osBindings/Cifs.ts diff --git a/sdk/base/lib/osBindings/ClearActionRequestsParams.ts b/sdk/base/lib/osBindings/ClearActionRequestsParams.ts new file mode 100644 index 000000000..856a13de4 --- /dev/null +++ b/sdk/base/lib/osBindings/ClearActionRequestsParams.ts @@ -0,0 +1,5 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ClearActionRequestsParams = + | { only: string[] } + | { except: string[] } diff --git a/sdk/base/lib/osBindings/ClearActionsParams.ts b/sdk/base/lib/osBindings/ClearActionsParams.ts new file mode 100644 index 000000000..68cff676a --- /dev/null +++ b/sdk/base/lib/osBindings/ClearActionsParams.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ActionId } from "./ActionId" + +export type ClearActionsParams = { except: Array } diff --git a/sdk/base/lib/osBindings/ClearBindingsParams.ts b/sdk/base/lib/osBindings/ClearBindingsParams.ts new file mode 100644 index 000000000..41f1d5741 --- /dev/null +++ b/sdk/base/lib/osBindings/ClearBindingsParams.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { BindId } from "./BindId" + +export type ClearBindingsParams = { except: Array } diff --git a/sdk/base/lib/osBindings/ClearCallbacksParams.ts b/sdk/base/lib/osBindings/ClearCallbacksParams.ts new file mode 100644 index 000000000..095a27f5e --- /dev/null +++ b/sdk/base/lib/osBindings/ClearCallbacksParams.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ClearCallbacksParams = { only: number[] } | { except: number[] } diff --git a/sdk/base/lib/osBindings/ClearServiceInterfacesParams.ts b/sdk/base/lib/osBindings/ClearServiceInterfacesParams.ts new file mode 100644 index 000000000..02c177978 --- /dev/null +++ b/sdk/base/lib/osBindings/ClearServiceInterfacesParams.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ServiceInterfaceId } from "./ServiceInterfaceId" + +export type ClearServiceInterfacesParams = { except: Array } diff --git a/sdk/lib/osBindings/ContactInfo.ts b/sdk/base/lib/osBindings/ContactInfo.ts similarity index 100% rename from sdk/lib/osBindings/ContactInfo.ts rename to sdk/base/lib/osBindings/ContactInfo.ts diff --git a/sdk/lib/osBindings/CreateSubcontainerFsParams.ts b/sdk/base/lib/osBindings/CreateSubcontainerFsParams.ts similarity index 100% rename from sdk/lib/osBindings/CreateSubcontainerFsParams.ts rename to sdk/base/lib/osBindings/CreateSubcontainerFsParams.ts diff --git a/sdk/lib/osBindings/CurrentDependencies.ts b/sdk/base/lib/osBindings/CurrentDependencies.ts similarity index 100% rename from sdk/lib/osBindings/CurrentDependencies.ts rename to sdk/base/lib/osBindings/CurrentDependencies.ts diff --git a/sdk/lib/osBindings/CurrentDependencyInfo.ts b/sdk/base/lib/osBindings/CurrentDependencyInfo.ts similarity index 92% rename from sdk/lib/osBindings/CurrentDependencyInfo.ts rename to sdk/base/lib/osBindings/CurrentDependencyInfo.ts index 2096a0113..e56e2be7a 100644 --- a/sdk/lib/osBindings/CurrentDependencyInfo.ts +++ b/sdk/base/lib/osBindings/CurrentDependencyInfo.ts @@ -5,5 +5,4 @@ export type CurrentDependencyInfo = { title: string | null icon: DataUrl | null versionRange: string - configSatisfied: boolean } & ({ kind: "exists" } | { kind: "running"; healthChecks: string[] }) diff --git a/sdk/lib/osBindings/DataUrl.ts b/sdk/base/lib/osBindings/DataUrl.ts similarity index 100% rename from sdk/lib/osBindings/DataUrl.ts rename to sdk/base/lib/osBindings/DataUrl.ts diff --git a/sdk/lib/osBindings/DepInfo.ts b/sdk/base/lib/osBindings/DepInfo.ts similarity index 100% rename from sdk/lib/osBindings/DepInfo.ts rename to sdk/base/lib/osBindings/DepInfo.ts diff --git a/sdk/lib/osBindings/Dependencies.ts b/sdk/base/lib/osBindings/Dependencies.ts similarity index 100% rename from sdk/lib/osBindings/Dependencies.ts rename to sdk/base/lib/osBindings/Dependencies.ts diff --git a/sdk/lib/osBindings/DependencyKind.ts b/sdk/base/lib/osBindings/DependencyKind.ts similarity index 100% rename from sdk/lib/osBindings/DependencyKind.ts rename to sdk/base/lib/osBindings/DependencyKind.ts diff --git a/sdk/lib/osBindings/DependencyMetadata.ts b/sdk/base/lib/osBindings/DependencyMetadata.ts similarity index 100% rename from sdk/lib/osBindings/DependencyMetadata.ts rename to sdk/base/lib/osBindings/DependencyMetadata.ts diff --git a/sdk/lib/osBindings/DependencyRequirement.ts b/sdk/base/lib/osBindings/DependencyRequirement.ts similarity index 100% rename from sdk/lib/osBindings/DependencyRequirement.ts rename to sdk/base/lib/osBindings/DependencyRequirement.ts diff --git a/sdk/lib/osBindings/Description.ts b/sdk/base/lib/osBindings/Description.ts similarity index 100% rename from sdk/lib/osBindings/Description.ts rename to sdk/base/lib/osBindings/Description.ts diff --git a/sdk/lib/osBindings/DestroySubcontainerFsParams.ts b/sdk/base/lib/osBindings/DestroySubcontainerFsParams.ts similarity index 100% rename from sdk/lib/osBindings/DestroySubcontainerFsParams.ts rename to sdk/base/lib/osBindings/DestroySubcontainerFsParams.ts diff --git a/sdk/lib/osBindings/Duration.ts b/sdk/base/lib/osBindings/Duration.ts similarity index 100% rename from sdk/lib/osBindings/Duration.ts rename to sdk/base/lib/osBindings/Duration.ts diff --git a/sdk/lib/osBindings/EchoParams.ts b/sdk/base/lib/osBindings/EchoParams.ts similarity index 100% rename from sdk/lib/osBindings/EchoParams.ts rename to sdk/base/lib/osBindings/EchoParams.ts diff --git a/sdk/lib/osBindings/EncryptedWire.ts b/sdk/base/lib/osBindings/EncryptedWire.ts similarity index 100% rename from sdk/lib/osBindings/EncryptedWire.ts rename to sdk/base/lib/osBindings/EncryptedWire.ts diff --git a/sdk/lib/osBindings/ExportActionParams.ts b/sdk/base/lib/osBindings/ExportActionParams.ts similarity index 58% rename from sdk/lib/osBindings/ExportActionParams.ts rename to sdk/base/lib/osBindings/ExportActionParams.ts index 8bcfbc349..d4aa33102 100644 --- a/sdk/lib/osBindings/ExportActionParams.ts +++ b/sdk/base/lib/osBindings/ExportActionParams.ts @@ -1,10 +1,5 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { ActionId } from "./ActionId" import type { ActionMetadata } from "./ActionMetadata" -import type { PackageId } from "./PackageId" -export type ExportActionParams = { - packageId?: PackageId - id: ActionId - metadata: ActionMetadata -} +export type ExportActionParams = { id: ActionId; metadata: ActionMetadata } diff --git a/sdk/lib/osBindings/ExportServiceInterfaceParams.ts b/sdk/base/lib/osBindings/ExportServiceInterfaceParams.ts similarity index 100% rename from sdk/lib/osBindings/ExportServiceInterfaceParams.ts rename to sdk/base/lib/osBindings/ExportServiceInterfaceParams.ts diff --git a/sdk/lib/osBindings/ExposeForDependentsParams.ts b/sdk/base/lib/osBindings/ExposeForDependentsParams.ts similarity index 100% rename from sdk/lib/osBindings/ExposeForDependentsParams.ts rename to sdk/base/lib/osBindings/ExposeForDependentsParams.ts diff --git a/sdk/lib/osBindings/FullIndex.ts b/sdk/base/lib/osBindings/FullIndex.ts similarity index 100% rename from sdk/lib/osBindings/FullIndex.ts rename to sdk/base/lib/osBindings/FullIndex.ts diff --git a/sdk/lib/osBindings/FullProgress.ts b/sdk/base/lib/osBindings/FullProgress.ts similarity index 100% rename from sdk/lib/osBindings/FullProgress.ts rename to sdk/base/lib/osBindings/FullProgress.ts diff --git a/sdk/lib/osBindings/GetConfiguredParams.ts b/sdk/base/lib/osBindings/GetActionInputParams.ts similarity index 55% rename from sdk/lib/osBindings/GetConfiguredParams.ts rename to sdk/base/lib/osBindings/GetActionInputParams.ts index 66fb6e320..568ceb907 100644 --- a/sdk/lib/osBindings/GetConfiguredParams.ts +++ b/sdk/base/lib/osBindings/GetActionInputParams.ts @@ -1,4 +1,5 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ActionId } from "./ActionId" import type { PackageId } from "./PackageId" -export type GetConfiguredParams = { packageId?: PackageId } +export type GetActionInputParams = { packageId?: PackageId; actionId: ActionId } diff --git a/sdk/lib/osBindings/GetHostInfoParams.ts b/sdk/base/lib/osBindings/GetHostInfoParams.ts similarity index 100% rename from sdk/lib/osBindings/GetHostInfoParams.ts rename to sdk/base/lib/osBindings/GetHostInfoParams.ts diff --git a/sdk/lib/osBindings/GetOsAssetParams.ts b/sdk/base/lib/osBindings/GetOsAssetParams.ts similarity index 100% rename from sdk/lib/osBindings/GetOsAssetParams.ts rename to sdk/base/lib/osBindings/GetOsAssetParams.ts diff --git a/sdk/lib/osBindings/GetOsVersionParams.ts b/sdk/base/lib/osBindings/GetOsVersionParams.ts similarity index 100% rename from sdk/lib/osBindings/GetOsVersionParams.ts rename to sdk/base/lib/osBindings/GetOsVersionParams.ts diff --git a/sdk/lib/osBindings/GetPackageParams.ts b/sdk/base/lib/osBindings/GetPackageParams.ts similarity index 100% rename from sdk/lib/osBindings/GetPackageParams.ts rename to sdk/base/lib/osBindings/GetPackageParams.ts diff --git a/sdk/lib/osBindings/GetPackageResponse.ts b/sdk/base/lib/osBindings/GetPackageResponse.ts similarity index 100% rename from sdk/lib/osBindings/GetPackageResponse.ts rename to sdk/base/lib/osBindings/GetPackageResponse.ts diff --git a/sdk/lib/osBindings/GetPackageResponseFull.ts b/sdk/base/lib/osBindings/GetPackageResponseFull.ts similarity index 100% rename from sdk/lib/osBindings/GetPackageResponseFull.ts rename to sdk/base/lib/osBindings/GetPackageResponseFull.ts diff --git a/sdk/lib/osBindings/GetPrimaryUrlParams.ts b/sdk/base/lib/osBindings/GetPrimaryUrlParams.ts similarity index 100% rename from sdk/lib/osBindings/GetPrimaryUrlParams.ts rename to sdk/base/lib/osBindings/GetPrimaryUrlParams.ts diff --git a/sdk/lib/osBindings/GetServiceInterfaceParams.ts b/sdk/base/lib/osBindings/GetServiceInterfaceParams.ts similarity index 100% rename from sdk/lib/osBindings/GetServiceInterfaceParams.ts rename to sdk/base/lib/osBindings/GetServiceInterfaceParams.ts diff --git a/sdk/lib/osBindings/GetServicePortForwardParams.ts b/sdk/base/lib/osBindings/GetServicePortForwardParams.ts similarity index 100% rename from sdk/lib/osBindings/GetServicePortForwardParams.ts rename to sdk/base/lib/osBindings/GetServicePortForwardParams.ts diff --git a/sdk/lib/osBindings/GetSslCertificateParams.ts b/sdk/base/lib/osBindings/GetSslCertificateParams.ts similarity index 100% rename from sdk/lib/osBindings/GetSslCertificateParams.ts rename to sdk/base/lib/osBindings/GetSslCertificateParams.ts diff --git a/sdk/lib/osBindings/GetSslKeyParams.ts b/sdk/base/lib/osBindings/GetSslKeyParams.ts similarity index 100% rename from sdk/lib/osBindings/GetSslKeyParams.ts rename to sdk/base/lib/osBindings/GetSslKeyParams.ts diff --git a/sdk/lib/osBindings/GetStoreParams.ts b/sdk/base/lib/osBindings/GetStoreParams.ts similarity index 100% rename from sdk/lib/osBindings/GetStoreParams.ts rename to sdk/base/lib/osBindings/GetStoreParams.ts diff --git a/sdk/lib/osBindings/GetSystemSmtpParams.ts b/sdk/base/lib/osBindings/GetSystemSmtpParams.ts similarity index 100% rename from sdk/lib/osBindings/GetSystemSmtpParams.ts rename to sdk/base/lib/osBindings/GetSystemSmtpParams.ts diff --git a/sdk/lib/osBindings/Governor.ts b/sdk/base/lib/osBindings/Governor.ts similarity index 100% rename from sdk/lib/osBindings/Governor.ts rename to sdk/base/lib/osBindings/Governor.ts diff --git a/sdk/lib/osBindings/Guid.ts b/sdk/base/lib/osBindings/Guid.ts similarity index 100% rename from sdk/lib/osBindings/Guid.ts rename to sdk/base/lib/osBindings/Guid.ts diff --git a/sdk/lib/osBindings/HardwareRequirements.ts b/sdk/base/lib/osBindings/HardwareRequirements.ts similarity index 100% rename from sdk/lib/osBindings/HardwareRequirements.ts rename to sdk/base/lib/osBindings/HardwareRequirements.ts diff --git a/sdk/lib/osBindings/HealthCheckId.ts b/sdk/base/lib/osBindings/HealthCheckId.ts similarity index 100% rename from sdk/lib/osBindings/HealthCheckId.ts rename to sdk/base/lib/osBindings/HealthCheckId.ts diff --git a/sdk/lib/osBindings/Host.ts b/sdk/base/lib/osBindings/Host.ts similarity index 100% rename from sdk/lib/osBindings/Host.ts rename to sdk/base/lib/osBindings/Host.ts diff --git a/sdk/lib/osBindings/HostAddress.ts b/sdk/base/lib/osBindings/HostAddress.ts similarity index 100% rename from sdk/lib/osBindings/HostAddress.ts rename to sdk/base/lib/osBindings/HostAddress.ts diff --git a/sdk/lib/osBindings/HostId.ts b/sdk/base/lib/osBindings/HostId.ts similarity index 100% rename from sdk/lib/osBindings/HostId.ts rename to sdk/base/lib/osBindings/HostId.ts diff --git a/sdk/lib/osBindings/HostKind.ts b/sdk/base/lib/osBindings/HostKind.ts similarity index 100% rename from sdk/lib/osBindings/HostKind.ts rename to sdk/base/lib/osBindings/HostKind.ts diff --git a/sdk/lib/osBindings/HostnameInfo.ts b/sdk/base/lib/osBindings/HostnameInfo.ts similarity index 100% rename from sdk/lib/osBindings/HostnameInfo.ts rename to sdk/base/lib/osBindings/HostnameInfo.ts diff --git a/sdk/lib/osBindings/Hosts.ts b/sdk/base/lib/osBindings/Hosts.ts similarity index 100% rename from sdk/lib/osBindings/Hosts.ts rename to sdk/base/lib/osBindings/Hosts.ts diff --git a/sdk/lib/osBindings/ImageConfig.ts b/sdk/base/lib/osBindings/ImageConfig.ts similarity index 100% rename from sdk/lib/osBindings/ImageConfig.ts rename to sdk/base/lib/osBindings/ImageConfig.ts diff --git a/sdk/lib/osBindings/ImageId.ts b/sdk/base/lib/osBindings/ImageId.ts similarity index 100% rename from sdk/lib/osBindings/ImageId.ts rename to sdk/base/lib/osBindings/ImageId.ts diff --git a/sdk/lib/osBindings/ImageMetadata.ts b/sdk/base/lib/osBindings/ImageMetadata.ts similarity index 100% rename from sdk/lib/osBindings/ImageMetadata.ts rename to sdk/base/lib/osBindings/ImageMetadata.ts diff --git a/sdk/lib/osBindings/ImageSource.ts b/sdk/base/lib/osBindings/ImageSource.ts similarity index 100% rename from sdk/lib/osBindings/ImageSource.ts rename to sdk/base/lib/osBindings/ImageSource.ts diff --git a/sdk/lib/osBindings/InitProgressRes.ts b/sdk/base/lib/osBindings/InitProgressRes.ts similarity index 100% rename from sdk/lib/osBindings/InitProgressRes.ts rename to sdk/base/lib/osBindings/InitProgressRes.ts diff --git a/sdk/lib/osBindings/InstallParams.ts b/sdk/base/lib/osBindings/InstallParams.ts similarity index 100% rename from sdk/lib/osBindings/InstallParams.ts rename to sdk/base/lib/osBindings/InstallParams.ts diff --git a/sdk/lib/osBindings/InstalledState.ts b/sdk/base/lib/osBindings/InstalledState.ts similarity index 100% rename from sdk/lib/osBindings/InstalledState.ts rename to sdk/base/lib/osBindings/InstalledState.ts diff --git a/sdk/lib/osBindings/InstalledVersionParams.ts b/sdk/base/lib/osBindings/InstalledVersionParams.ts similarity index 100% rename from sdk/lib/osBindings/InstalledVersionParams.ts rename to sdk/base/lib/osBindings/InstalledVersionParams.ts diff --git a/sdk/lib/osBindings/InstallingInfo.ts b/sdk/base/lib/osBindings/InstallingInfo.ts similarity index 100% rename from sdk/lib/osBindings/InstallingInfo.ts rename to sdk/base/lib/osBindings/InstallingInfo.ts diff --git a/sdk/lib/osBindings/InstallingState.ts b/sdk/base/lib/osBindings/InstallingState.ts similarity index 100% rename from sdk/lib/osBindings/InstallingState.ts rename to sdk/base/lib/osBindings/InstallingState.ts diff --git a/sdk/lib/osBindings/IpHostname.ts b/sdk/base/lib/osBindings/IpHostname.ts similarity index 100% rename from sdk/lib/osBindings/IpHostname.ts rename to sdk/base/lib/osBindings/IpHostname.ts diff --git a/sdk/lib/osBindings/IpInfo.ts b/sdk/base/lib/osBindings/IpInfo.ts similarity index 100% rename from sdk/lib/osBindings/IpInfo.ts rename to sdk/base/lib/osBindings/IpInfo.ts diff --git a/sdk/lib/osBindings/LanInfo.ts b/sdk/base/lib/osBindings/LanInfo.ts similarity index 100% rename from sdk/lib/osBindings/LanInfo.ts rename to sdk/base/lib/osBindings/LanInfo.ts diff --git a/sdk/lib/osBindings/ListServiceInterfacesParams.ts b/sdk/base/lib/osBindings/ListServiceInterfacesParams.ts similarity index 100% rename from sdk/lib/osBindings/ListServiceInterfacesParams.ts rename to sdk/base/lib/osBindings/ListServiceInterfacesParams.ts diff --git a/sdk/lib/osBindings/ListVersionSignersParams.ts b/sdk/base/lib/osBindings/ListVersionSignersParams.ts similarity index 100% rename from sdk/lib/osBindings/ListVersionSignersParams.ts rename to sdk/base/lib/osBindings/ListVersionSignersParams.ts diff --git a/sdk/lib/osBindings/LoginParams.ts b/sdk/base/lib/osBindings/LoginParams.ts similarity index 100% rename from sdk/lib/osBindings/LoginParams.ts rename to sdk/base/lib/osBindings/LoginParams.ts diff --git a/sdk/lib/osBindings/MainStatus.ts b/sdk/base/lib/osBindings/MainStatus.ts similarity index 69% rename from sdk/lib/osBindings/MainStatus.ts rename to sdk/base/lib/osBindings/MainStatus.ts index 1f6b3babe..dbd9a8fcc 100644 --- a/sdk/lib/osBindings/MainStatus.ts +++ b/sdk/base/lib/osBindings/MainStatus.ts @@ -4,17 +4,17 @@ import type { NamedHealthCheckResult } from "./NamedHealthCheckResult" import type { StartStop } from "./StartStop" export type MainStatus = - | { status: "stopped" } - | { status: "restarting" } - | { status: "restoring" } - | { status: "stopping" } + | { main: "stopped" } + | { main: "restarting" } + | { main: "restoring" } + | { main: "stopping" } | { - status: "starting" + main: "starting" health: { [key: HealthCheckId]: NamedHealthCheckResult } } | { - status: "running" + main: "running" started: string health: { [key: HealthCheckId]: NamedHealthCheckResult } } - | { status: "backingUp"; onComplete: StartStop } + | { main: "backingUp"; onComplete: StartStop } diff --git a/sdk/lib/osBindings/Manifest.ts b/sdk/base/lib/osBindings/Manifest.ts similarity index 98% rename from sdk/lib/osBindings/Manifest.ts rename to sdk/base/lib/osBindings/Manifest.ts index d40223236..8007b565b 100644 --- a/sdk/lib/osBindings/Manifest.ts +++ b/sdk/base/lib/osBindings/Manifest.ts @@ -32,5 +32,4 @@ export type Manifest = { hardwareRequirements: HardwareRequirements gitHash: string | null osVersion: string - hasConfig: boolean } diff --git a/sdk/lib/osBindings/MaybeUtf8String.ts b/sdk/base/lib/osBindings/MaybeUtf8String.ts similarity index 100% rename from sdk/lib/osBindings/MaybeUtf8String.ts rename to sdk/base/lib/osBindings/MaybeUtf8String.ts diff --git a/sdk/lib/osBindings/MerkleArchiveCommitment.ts b/sdk/base/lib/osBindings/MerkleArchiveCommitment.ts similarity index 100% rename from sdk/lib/osBindings/MerkleArchiveCommitment.ts rename to sdk/base/lib/osBindings/MerkleArchiveCommitment.ts diff --git a/sdk/lib/osBindings/MountParams.ts b/sdk/base/lib/osBindings/MountParams.ts similarity index 100% rename from sdk/lib/osBindings/MountParams.ts rename to sdk/base/lib/osBindings/MountParams.ts diff --git a/sdk/lib/osBindings/MountTarget.ts b/sdk/base/lib/osBindings/MountTarget.ts similarity index 100% rename from sdk/lib/osBindings/MountTarget.ts rename to sdk/base/lib/osBindings/MountTarget.ts diff --git a/sdk/lib/osBindings/NamedHealthCheckResult.ts b/sdk/base/lib/osBindings/NamedHealthCheckResult.ts similarity index 100% rename from sdk/lib/osBindings/NamedHealthCheckResult.ts rename to sdk/base/lib/osBindings/NamedHealthCheckResult.ts diff --git a/sdk/lib/osBindings/NamedProgress.ts b/sdk/base/lib/osBindings/NamedProgress.ts similarity index 100% rename from sdk/lib/osBindings/NamedProgress.ts rename to sdk/base/lib/osBindings/NamedProgress.ts diff --git a/sdk/lib/osBindings/OnionHostname.ts b/sdk/base/lib/osBindings/OnionHostname.ts similarity index 100% rename from sdk/lib/osBindings/OnionHostname.ts rename to sdk/base/lib/osBindings/OnionHostname.ts diff --git a/sdk/lib/osBindings/OsIndex.ts b/sdk/base/lib/osBindings/OsIndex.ts similarity index 100% rename from sdk/lib/osBindings/OsIndex.ts rename to sdk/base/lib/osBindings/OsIndex.ts diff --git a/sdk/lib/osBindings/OsVersionInfo.ts b/sdk/base/lib/osBindings/OsVersionInfo.ts similarity index 100% rename from sdk/lib/osBindings/OsVersionInfo.ts rename to sdk/base/lib/osBindings/OsVersionInfo.ts diff --git a/sdk/lib/osBindings/OsVersionInfoMap.ts b/sdk/base/lib/osBindings/OsVersionInfoMap.ts similarity index 100% rename from sdk/lib/osBindings/OsVersionInfoMap.ts rename to sdk/base/lib/osBindings/OsVersionInfoMap.ts diff --git a/sdk/lib/osBindings/PackageDataEntry.ts b/sdk/base/lib/osBindings/PackageDataEntry.ts similarity index 83% rename from sdk/lib/osBindings/PackageDataEntry.ts rename to sdk/base/lib/osBindings/PackageDataEntry.ts index 41bd98bba..86df7b767 100644 --- a/sdk/lib/osBindings/PackageDataEntry.ts +++ b/sdk/base/lib/osBindings/PackageDataEntry.ts @@ -1,25 +1,27 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { ActionId } from "./ActionId" import type { ActionMetadata } from "./ActionMetadata" +import type { ActionRequestEntry } from "./ActionRequestEntry" import type { CurrentDependencies } from "./CurrentDependencies" import type { DataUrl } from "./DataUrl" import type { Hosts } from "./Hosts" +import type { MainStatus } from "./MainStatus" import type { PackageState } from "./PackageState" import type { ServiceInterface } from "./ServiceInterface" import type { ServiceInterfaceId } from "./ServiceInterfaceId" -import type { Status } from "./Status" import type { Version } from "./Version" export type PackageDataEntry = { stateInfo: PackageState dataVersion: Version | null - status: Status + status: MainStatus registry: string | null developerKey: string icon: DataUrl lastBackup: string | null currentDependencies: CurrentDependencies actions: { [key: ActionId]: ActionMetadata } + requestedActions: { [key: string]: ActionRequestEntry } serviceInterfaces: { [key: ServiceInterfaceId]: ServiceInterface } hosts: Hosts storeExposedDependents: string[] diff --git a/sdk/lib/osBindings/PackageDetailLevel.ts b/sdk/base/lib/osBindings/PackageDetailLevel.ts similarity index 100% rename from sdk/lib/osBindings/PackageDetailLevel.ts rename to sdk/base/lib/osBindings/PackageDetailLevel.ts diff --git a/sdk/lib/osBindings/PackageId.ts b/sdk/base/lib/osBindings/PackageId.ts similarity index 100% rename from sdk/lib/osBindings/PackageId.ts rename to sdk/base/lib/osBindings/PackageId.ts diff --git a/sdk/lib/osBindings/PackageIndex.ts b/sdk/base/lib/osBindings/PackageIndex.ts similarity index 100% rename from sdk/lib/osBindings/PackageIndex.ts rename to sdk/base/lib/osBindings/PackageIndex.ts diff --git a/sdk/lib/osBindings/PackageInfo.ts b/sdk/base/lib/osBindings/PackageInfo.ts similarity index 100% rename from sdk/lib/osBindings/PackageInfo.ts rename to sdk/base/lib/osBindings/PackageInfo.ts diff --git a/sdk/lib/osBindings/PackageInfoShort.ts b/sdk/base/lib/osBindings/PackageInfoShort.ts similarity index 100% rename from sdk/lib/osBindings/PackageInfoShort.ts rename to sdk/base/lib/osBindings/PackageInfoShort.ts diff --git a/sdk/lib/osBindings/PackageState.ts b/sdk/base/lib/osBindings/PackageState.ts similarity index 100% rename from sdk/lib/osBindings/PackageState.ts rename to sdk/base/lib/osBindings/PackageState.ts diff --git a/sdk/lib/osBindings/PackageVersionInfo.ts b/sdk/base/lib/osBindings/PackageVersionInfo.ts similarity index 100% rename from sdk/lib/osBindings/PackageVersionInfo.ts rename to sdk/base/lib/osBindings/PackageVersionInfo.ts diff --git a/sdk/lib/osBindings/PasswordType.ts b/sdk/base/lib/osBindings/PasswordType.ts similarity index 100% rename from sdk/lib/osBindings/PasswordType.ts rename to sdk/base/lib/osBindings/PasswordType.ts diff --git a/sdk/lib/osBindings/PathOrUrl.ts b/sdk/base/lib/osBindings/PathOrUrl.ts similarity index 100% rename from sdk/lib/osBindings/PathOrUrl.ts rename to sdk/base/lib/osBindings/PathOrUrl.ts diff --git a/sdk/lib/osBindings/ProcedureId.ts b/sdk/base/lib/osBindings/ProcedureId.ts similarity index 100% rename from sdk/lib/osBindings/ProcedureId.ts rename to sdk/base/lib/osBindings/ProcedureId.ts diff --git a/sdk/lib/osBindings/Progress.ts b/sdk/base/lib/osBindings/Progress.ts similarity index 100% rename from sdk/lib/osBindings/Progress.ts rename to sdk/base/lib/osBindings/Progress.ts diff --git a/sdk/lib/osBindings/Public.ts b/sdk/base/lib/osBindings/Public.ts similarity index 100% rename from sdk/lib/osBindings/Public.ts rename to sdk/base/lib/osBindings/Public.ts diff --git a/sdk/lib/osBindings/RecoverySource.ts b/sdk/base/lib/osBindings/RecoverySource.ts similarity index 100% rename from sdk/lib/osBindings/RecoverySource.ts rename to sdk/base/lib/osBindings/RecoverySource.ts diff --git a/sdk/lib/osBindings/RegistryAsset.ts b/sdk/base/lib/osBindings/RegistryAsset.ts similarity index 100% rename from sdk/lib/osBindings/RegistryAsset.ts rename to sdk/base/lib/osBindings/RegistryAsset.ts diff --git a/sdk/lib/osBindings/RegistryInfo.ts b/sdk/base/lib/osBindings/RegistryInfo.ts similarity index 100% rename from sdk/lib/osBindings/RegistryInfo.ts rename to sdk/base/lib/osBindings/RegistryInfo.ts diff --git a/sdk/lib/osBindings/RemoveVersionParams.ts b/sdk/base/lib/osBindings/RemoveVersionParams.ts similarity index 100% rename from sdk/lib/osBindings/RemoveVersionParams.ts rename to sdk/base/lib/osBindings/RemoveVersionParams.ts diff --git a/sdk/lib/osBindings/SetConfigured.ts b/sdk/base/lib/osBindings/ReplayId.ts similarity index 67% rename from sdk/lib/osBindings/SetConfigured.ts rename to sdk/base/lib/osBindings/ReplayId.ts index e1478422a..048ef183a 100644 --- a/sdk/lib/osBindings/SetConfigured.ts +++ b/sdk/base/lib/osBindings/ReplayId.ts @@ -1,3 +1,3 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type SetConfigured = { configured: boolean } +export type ReplayId = string diff --git a/sdk/base/lib/osBindings/RequestActionParams.ts b/sdk/base/lib/osBindings/RequestActionParams.ts new file mode 100644 index 000000000..db83be595 --- /dev/null +++ b/sdk/base/lib/osBindings/RequestActionParams.ts @@ -0,0 +1,15 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ActionId } from "./ActionId" +import type { ActionRequestInput } from "./ActionRequestInput" +import type { ActionRequestTrigger } from "./ActionRequestTrigger" +import type { PackageId } from "./PackageId" +import type { ReplayId } from "./ReplayId" + +export type RequestActionParams = { + replayId: ReplayId + packageId: PackageId + actionId: ActionId + description?: string + when?: ActionRequestTrigger + input?: ActionRequestInput +} diff --git a/sdk/lib/osBindings/RequestCommitment.ts b/sdk/base/lib/osBindings/RequestCommitment.ts similarity index 100% rename from sdk/lib/osBindings/RequestCommitment.ts rename to sdk/base/lib/osBindings/RequestCommitment.ts diff --git a/sdk/lib/osBindings/ExecuteAction.ts b/sdk/base/lib/osBindings/RunActionParams.ts similarity index 88% rename from sdk/lib/osBindings/ExecuteAction.ts rename to sdk/base/lib/osBindings/RunActionParams.ts index 6e3c44f79..33864d1e6 100644 --- a/sdk/lib/osBindings/ExecuteAction.ts +++ b/sdk/base/lib/osBindings/RunActionParams.ts @@ -2,7 +2,7 @@ import type { ActionId } from "./ActionId" import type { PackageId } from "./PackageId" -export type ExecuteAction = { +export type RunActionParams = { packageId?: PackageId actionId: ActionId input: any diff --git a/sdk/lib/osBindings/Security.ts b/sdk/base/lib/osBindings/Security.ts similarity index 100% rename from sdk/lib/osBindings/Security.ts rename to sdk/base/lib/osBindings/Security.ts diff --git a/sdk/lib/osBindings/ServerInfo.ts b/sdk/base/lib/osBindings/ServerInfo.ts similarity index 100% rename from sdk/lib/osBindings/ServerInfo.ts rename to sdk/base/lib/osBindings/ServerInfo.ts diff --git a/sdk/lib/osBindings/ServerSpecs.ts b/sdk/base/lib/osBindings/ServerSpecs.ts similarity index 100% rename from sdk/lib/osBindings/ServerSpecs.ts rename to sdk/base/lib/osBindings/ServerSpecs.ts diff --git a/sdk/lib/osBindings/ServerStatus.ts b/sdk/base/lib/osBindings/ServerStatus.ts similarity index 100% rename from sdk/lib/osBindings/ServerStatus.ts rename to sdk/base/lib/osBindings/ServerStatus.ts diff --git a/sdk/lib/osBindings/ServiceInterface.ts b/sdk/base/lib/osBindings/ServiceInterface.ts similarity index 100% rename from sdk/lib/osBindings/ServiceInterface.ts rename to sdk/base/lib/osBindings/ServiceInterface.ts diff --git a/sdk/lib/osBindings/ServiceInterfaceId.ts b/sdk/base/lib/osBindings/ServiceInterfaceId.ts similarity index 100% rename from sdk/lib/osBindings/ServiceInterfaceId.ts rename to sdk/base/lib/osBindings/ServiceInterfaceId.ts diff --git a/sdk/lib/osBindings/ServiceInterfaceType.ts b/sdk/base/lib/osBindings/ServiceInterfaceType.ts similarity index 100% rename from sdk/lib/osBindings/ServiceInterfaceType.ts rename to sdk/base/lib/osBindings/ServiceInterfaceType.ts diff --git a/sdk/lib/osBindings/Session.ts b/sdk/base/lib/osBindings/Session.ts similarity index 100% rename from sdk/lib/osBindings/Session.ts rename to sdk/base/lib/osBindings/Session.ts diff --git a/sdk/lib/osBindings/SessionList.ts b/sdk/base/lib/osBindings/SessionList.ts similarity index 100% rename from sdk/lib/osBindings/SessionList.ts rename to sdk/base/lib/osBindings/SessionList.ts diff --git a/sdk/lib/osBindings/Sessions.ts b/sdk/base/lib/osBindings/Sessions.ts similarity index 100% rename from sdk/lib/osBindings/Sessions.ts rename to sdk/base/lib/osBindings/Sessions.ts diff --git a/sdk/lib/osBindings/SetDataVersionParams.ts b/sdk/base/lib/osBindings/SetDataVersionParams.ts similarity index 100% rename from sdk/lib/osBindings/SetDataVersionParams.ts rename to sdk/base/lib/osBindings/SetDataVersionParams.ts diff --git a/sdk/lib/osBindings/SetDependenciesParams.ts b/sdk/base/lib/osBindings/SetDependenciesParams.ts similarity index 82% rename from sdk/lib/osBindings/SetDependenciesParams.ts rename to sdk/base/lib/osBindings/SetDependenciesParams.ts index bbc9b325f..7b34b50c9 100644 --- a/sdk/lib/osBindings/SetDependenciesParams.ts +++ b/sdk/base/lib/osBindings/SetDependenciesParams.ts @@ -1,8 +1,6 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { DependencyRequirement } from "./DependencyRequirement" -import type { Guid } from "./Guid" export type SetDependenciesParams = { - procedureId: Guid dependencies: Array } diff --git a/sdk/lib/osBindings/SetHealth.ts b/sdk/base/lib/osBindings/SetHealth.ts similarity index 100% rename from sdk/lib/osBindings/SetHealth.ts rename to sdk/base/lib/osBindings/SetHealth.ts diff --git a/sdk/lib/osBindings/SetMainStatus.ts b/sdk/base/lib/osBindings/SetMainStatus.ts similarity index 100% rename from sdk/lib/osBindings/SetMainStatus.ts rename to sdk/base/lib/osBindings/SetMainStatus.ts diff --git a/sdk/lib/osBindings/SetMainStatusStatus.ts b/sdk/base/lib/osBindings/SetMainStatusStatus.ts similarity index 100% rename from sdk/lib/osBindings/SetMainStatusStatus.ts rename to sdk/base/lib/osBindings/SetMainStatusStatus.ts diff --git a/sdk/lib/osBindings/SetStoreParams.ts b/sdk/base/lib/osBindings/SetStoreParams.ts similarity index 100% rename from sdk/lib/osBindings/SetStoreParams.ts rename to sdk/base/lib/osBindings/SetStoreParams.ts diff --git a/sdk/lib/osBindings/SetupExecuteParams.ts b/sdk/base/lib/osBindings/SetupExecuteParams.ts similarity index 100% rename from sdk/lib/osBindings/SetupExecuteParams.ts rename to sdk/base/lib/osBindings/SetupExecuteParams.ts diff --git a/sdk/lib/osBindings/SetupProgress.ts b/sdk/base/lib/osBindings/SetupProgress.ts similarity index 100% rename from sdk/lib/osBindings/SetupProgress.ts rename to sdk/base/lib/osBindings/SetupProgress.ts diff --git a/sdk/lib/osBindings/SetupResult.ts b/sdk/base/lib/osBindings/SetupResult.ts similarity index 100% rename from sdk/lib/osBindings/SetupResult.ts rename to sdk/base/lib/osBindings/SetupResult.ts diff --git a/sdk/lib/osBindings/SetupStatusRes.ts b/sdk/base/lib/osBindings/SetupStatusRes.ts similarity index 100% rename from sdk/lib/osBindings/SetupStatusRes.ts rename to sdk/base/lib/osBindings/SetupStatusRes.ts diff --git a/sdk/lib/osBindings/SignAssetParams.ts b/sdk/base/lib/osBindings/SignAssetParams.ts similarity index 100% rename from sdk/lib/osBindings/SignAssetParams.ts rename to sdk/base/lib/osBindings/SignAssetParams.ts diff --git a/sdk/lib/osBindings/SignerInfo.ts b/sdk/base/lib/osBindings/SignerInfo.ts similarity index 100% rename from sdk/lib/osBindings/SignerInfo.ts rename to sdk/base/lib/osBindings/SignerInfo.ts diff --git a/sdk/lib/osBindings/SmtpValue.ts b/sdk/base/lib/osBindings/SmtpValue.ts similarity index 100% rename from sdk/lib/osBindings/SmtpValue.ts rename to sdk/base/lib/osBindings/SmtpValue.ts diff --git a/sdk/lib/osBindings/StartStop.ts b/sdk/base/lib/osBindings/StartStop.ts similarity index 100% rename from sdk/lib/osBindings/StartStop.ts rename to sdk/base/lib/osBindings/StartStop.ts diff --git a/sdk/lib/osBindings/UpdatingState.ts b/sdk/base/lib/osBindings/UpdatingState.ts similarity index 100% rename from sdk/lib/osBindings/UpdatingState.ts rename to sdk/base/lib/osBindings/UpdatingState.ts diff --git a/sdk/lib/osBindings/VerifyCifsParams.ts b/sdk/base/lib/osBindings/VerifyCifsParams.ts similarity index 100% rename from sdk/lib/osBindings/VerifyCifsParams.ts rename to sdk/base/lib/osBindings/VerifyCifsParams.ts diff --git a/sdk/lib/osBindings/Version.ts b/sdk/base/lib/osBindings/Version.ts similarity index 100% rename from sdk/lib/osBindings/Version.ts rename to sdk/base/lib/osBindings/Version.ts diff --git a/sdk/lib/osBindings/VersionSignerParams.ts b/sdk/base/lib/osBindings/VersionSignerParams.ts similarity index 100% rename from sdk/lib/osBindings/VersionSignerParams.ts rename to sdk/base/lib/osBindings/VersionSignerParams.ts diff --git a/sdk/lib/osBindings/VolumeId.ts b/sdk/base/lib/osBindings/VolumeId.ts similarity index 100% rename from sdk/lib/osBindings/VolumeId.ts rename to sdk/base/lib/osBindings/VolumeId.ts diff --git a/sdk/lib/osBindings/WifiInfo.ts b/sdk/base/lib/osBindings/WifiInfo.ts similarity index 100% rename from sdk/lib/osBindings/WifiInfo.ts rename to sdk/base/lib/osBindings/WifiInfo.ts diff --git a/sdk/lib/osBindings/index.ts b/sdk/base/lib/osBindings/index.ts similarity index 88% rename from sdk/lib/osBindings/index.ts rename to sdk/base/lib/osBindings/index.ts index 9492fe796..4b6af0ae1 100644 --- a/sdk/lib/osBindings/index.ts +++ b/sdk/base/lib/osBindings/index.ts @@ -1,6 +1,13 @@ export { AcceptSigners } from "./AcceptSigners" export { ActionId } from "./ActionId" +export { ActionInput } from "./ActionInput" export { ActionMetadata } from "./ActionMetadata" +export { ActionRequestCondition } from "./ActionRequestCondition" +export { ActionRequestEntry } from "./ActionRequestEntry" +export { ActionRequestInput } from "./ActionRequestInput" +export { ActionRequestTrigger } from "./ActionRequestTrigger" +export { ActionRequest } from "./ActionRequest" +export { ActionVisibility } from "./ActionVisibility" export { AddAdminParams } from "./AddAdminParams" export { AddAssetParams } from "./AddAssetParams" export { AddPackageParams } from "./AddPackageParams" @@ -20,6 +27,7 @@ export { AttachParams } from "./AttachParams" export { BackupProgress } from "./BackupProgress" export { BackupTargetFS } from "./BackupTargetFS" export { Base64 } from "./Base64" +export { BindId } from "./BindId" export { BindInfo } from "./BindInfo" export { BindOptions } from "./BindOptions" export { BindParams } from "./BindParams" @@ -30,6 +38,11 @@ export { Category } from "./Category" export { CheckDependenciesParam } from "./CheckDependenciesParam" export { CheckDependenciesResult } from "./CheckDependenciesResult" export { Cifs } from "./Cifs" +export { ClearActionRequestsParams } from "./ClearActionRequestsParams" +export { ClearActionsParams } from "./ClearActionsParams" +export { ClearBindingsParams } from "./ClearBindingsParams" +export { ClearCallbacksParams } from "./ClearCallbacksParams" +export { ClearServiceInterfacesParams } from "./ClearServiceInterfacesParams" export { ContactInfo } from "./ContactInfo" export { CreateSubcontainerFsParams } from "./CreateSubcontainerFsParams" export { CurrentDependencies } from "./CurrentDependencies" @@ -45,13 +58,12 @@ export { DestroySubcontainerFsParams } from "./DestroySubcontainerFsParams" export { Duration } from "./Duration" export { EchoParams } from "./EchoParams" export { EncryptedWire } from "./EncryptedWire" -export { ExecuteAction } from "./ExecuteAction" export { ExportActionParams } from "./ExportActionParams" export { ExportServiceInterfaceParams } from "./ExportServiceInterfaceParams" export { ExposeForDependentsParams } from "./ExposeForDependentsParams" export { FullIndex } from "./FullIndex" export { FullProgress } from "./FullProgress" -export { GetConfiguredParams } from "./GetConfiguredParams" +export { GetActionInputParams } from "./GetActionInputParams" export { GetHostInfoParams } from "./GetHostInfoParams" export { GetOsAssetParams } from "./GetOsAssetParams" export { GetOsVersionParams } from "./GetOsVersionParams" @@ -120,7 +132,10 @@ export { RecoverySource } from "./RecoverySource" export { RegistryAsset } from "./RegistryAsset" export { RegistryInfo } from "./RegistryInfo" export { RemoveVersionParams } from "./RemoveVersionParams" +export { ReplayId } from "./ReplayId" +export { RequestActionParams } from "./RequestActionParams" export { RequestCommitment } from "./RequestCommitment" +export { RunActionParams } from "./RunActionParams" export { Security } from "./Security" export { ServerInfo } from "./ServerInfo" export { ServerSpecs } from "./ServerSpecs" @@ -131,7 +146,6 @@ export { ServiceInterfaceType } from "./ServiceInterfaceType" export { SessionList } from "./SessionList" export { Sessions } from "./Sessions" export { Session } from "./Session" -export { SetConfigured } from "./SetConfigured" export { SetDataVersionParams } from "./SetDataVersionParams" export { SetDependenciesParams } from "./SetDependenciesParams" export { SetHealth } from "./SetHealth" @@ -146,7 +160,6 @@ export { SignAssetParams } from "./SignAssetParams" export { SignerInfo } from "./SignerInfo" export { SmtpValue } from "./SmtpValue" export { StartStop } from "./StartStop" -export { Status } from "./Status" export { UpdatingState } from "./UpdatingState" export { VerifyCifsParams } from "./VerifyCifsParams" export { VersionSignerParams } from "./VersionSignerParams" diff --git a/sdk/lib/s9pk/index.ts b/sdk/base/lib/s9pk/index.ts similarity index 100% rename from sdk/lib/s9pk/index.ts rename to sdk/base/lib/s9pk/index.ts diff --git a/sdk/lib/s9pk/merkleArchive/directoryContents.ts b/sdk/base/lib/s9pk/merkleArchive/directoryContents.ts similarity index 100% rename from sdk/lib/s9pk/merkleArchive/directoryContents.ts rename to sdk/base/lib/s9pk/merkleArchive/directoryContents.ts diff --git a/sdk/lib/s9pk/merkleArchive/fileContents.ts b/sdk/base/lib/s9pk/merkleArchive/fileContents.ts similarity index 100% rename from sdk/lib/s9pk/merkleArchive/fileContents.ts rename to sdk/base/lib/s9pk/merkleArchive/fileContents.ts diff --git a/sdk/lib/s9pk/merkleArchive/index.ts b/sdk/base/lib/s9pk/merkleArchive/index.ts similarity index 100% rename from sdk/lib/s9pk/merkleArchive/index.ts rename to sdk/base/lib/s9pk/merkleArchive/index.ts diff --git a/sdk/lib/s9pk/merkleArchive/varint.ts b/sdk/base/lib/s9pk/merkleArchive/varint.ts similarity index 96% rename from sdk/lib/s9pk/merkleArchive/varint.ts rename to sdk/base/lib/s9pk/merkleArchive/varint.ts index 016505307..a6a425289 100644 --- a/sdk/lib/s9pk/merkleArchive/varint.ts +++ b/sdk/base/lib/s9pk/merkleArchive/varint.ts @@ -1,4 +1,4 @@ -import { asError } from "../../util/asError" +import { asError } from "../../util" const msb = 0x80 const dropMsb = 0x7f diff --git a/sdk/lib/test/exverList.test.ts b/sdk/base/lib/test/exverList.test.ts similarity index 100% rename from sdk/lib/test/exverList.test.ts rename to sdk/base/lib/test/exverList.test.ts diff --git a/sdk/lib/test/graph.test.ts b/sdk/base/lib/test/graph.test.ts similarity index 99% rename from sdk/lib/test/graph.test.ts rename to sdk/base/lib/test/graph.test.ts index 7f02adc2e..a738123d6 100644 --- a/sdk/lib/test/graph.test.ts +++ b/sdk/base/lib/test/graph.test.ts @@ -1,4 +1,4 @@ -import { Graph } from "../util/graph" +import { Graph } from "../util" describe("graph", () => { { diff --git a/sdk/lib/test/configTypes.test.ts b/sdk/base/lib/test/inputSpecTypes.test.ts similarity index 64% rename from sdk/lib/test/configTypes.test.ts rename to sdk/base/lib/test/inputSpecTypes.test.ts index 6d0b9a3d8..6767faf20 100644 --- a/sdk/lib/test/configTypes.test.ts +++ b/sdk/base/lib/test/inputSpecTypes.test.ts @@ -1,15 +1,18 @@ -import { ListValueSpecOf, isValueSpecListOf } from "../config/configTypes" -import { Config } from "../config/builder/config" -import { List } from "../config/builder/list" -import { Value } from "../config/builder/value" +import { + ListValueSpecOf, + isValueSpecListOf, +} from "../actions/input/inputSpecTypes" +import { InputSpec } from "../actions/input/builder/inputSpec" +import { List } from "../actions/input/builder/list" +import { Value } from "../actions/input/builder/value" -describe("Config Types", () => { +describe("InputSpec Types", () => { test("isValueSpecListOf", async () => { const options = [List.obj, List.text] for (const option of options) { const test = (option as any)( {} as any, - { spec: Config.of({}) } as any, + { spec: InputSpec.of({}) } as any, ) as any const someList = await Value.list(test).build({} as any) if (isValueSpecListOf(someList, "text")) { diff --git a/sdk/lib/test/startosTypeValidation.test.ts b/sdk/base/lib/test/startosTypeValidation.test.ts similarity index 81% rename from sdk/lib/test/startosTypeValidation.test.ts rename to sdk/base/lib/test/startosTypeValidation.test.ts index bcbc4b6ec..7686328bd 100644 --- a/sdk/lib/test/startosTypeValidation.test.ts +++ b/sdk/base/lib/test/startosTypeValidation.test.ts @@ -1,9 +1,15 @@ import { Effects } from "../types" import { CheckDependenciesParam, - ExecuteAction, - GetConfiguredParams, + ClearActionRequestsParams, + ClearActionsParams, + ClearBindingsParams, + ClearCallbacksParams, + ClearServiceInterfacesParams, + GetActionInputParams, GetStoreParams, + RequestActionParams, + RunActionParams, SetDataVersionParams, SetMainStatus, SetStoreParams, @@ -12,7 +18,6 @@ import { CreateSubcontainerFsParams } from ".././osBindings" import { DestroySubcontainerFsParams } from ".././osBindings" import { BindParams } from ".././osBindings" import { GetHostInfoParams } from ".././osBindings" -import { SetConfigured } from ".././osBindings" import { SetHealth } from ".././osBindings" import { ExposeForDependentsParams } from ".././osBindings" import { GetSslCertificateParams } from ".././osBindings" @@ -41,21 +46,27 @@ type EffectsTypeChecker = { describe("startosTypeValidation ", () => { test(`checking the params match`, () => { - const testInput: any = {} typeEquality({ - executeAction: {} as ExecuteAction, + constRetry: {}, + clearCallbacks: {} as ClearCallbacksParams, + action: { + clear: {} as ClearActionsParams, + export: {} as ExportActionParams, + getInput: {} as GetActionInputParams, + run: {} as RunActionParams, + request: {} as RequestActionParams, + clearRequests: {} as ClearActionRequestsParams, + }, subcontainer: { createFs: {} as CreateSubcontainerFsParams, destroyFs: {} as DestroySubcontainerFsParams, }, - clearBindings: undefined, + clearBindings: {} as ClearBindingsParams, getInstalledPackages: undefined, bind: {} as BindParams, getHostInfo: {} as WithCallback, - getConfigured: {} as GetConfiguredParams, restart: undefined, shutdown: undefined, - setConfigured: {} as SetConfigured, setDataVersion: {} as SetDataVersionParams, getDataVersion: undefined, setHealth: {} as SetHealth, @@ -71,19 +82,14 @@ describe("startosTypeValidation ", () => { getSystemSmtp: {} as WithCallback, getContainerIp: undefined, getServicePortForward: {} as GetServicePortForwardParams, - clearServiceInterfaces: undefined, + clearServiceInterfaces: {} as ClearServiceInterfacesParams, exportServiceInterface: {} as ExportServiceInterfaceParams, getPrimaryUrl: {} as WithCallback, listServiceInterfaces: {} as WithCallback, - exportAction: {} as ExportActionParams, - clearActions: undefined, mount: {} as MountParams, checkDependencies: {} as CheckDependenciesParam, getDependencies: undefined, setMainStatus: {} as SetMainStatus, }) - typeEquality[0]>( - testInput as ExecuteAction, - ) }) }) diff --git a/sdk/lib/test/util.deepMerge.test.ts b/sdk/base/lib/test/util.deepMerge.test.ts similarity index 70% rename from sdk/lib/test/util.deepMerge.test.ts rename to sdk/base/lib/test/util.deepMerge.test.ts index 25a4a7d22..74ff92c26 100644 --- a/sdk/lib/test/util.deepMerge.test.ts +++ b/sdk/base/lib/test/util.deepMerge.test.ts @@ -1,5 +1,5 @@ -import { deepEqual } from "../util/deepEqual" -import { deepMerge } from "../util/deepMerge" +import { deepEqual } from "../util" +import { deepMerge } from "../util" describe("deepMerge", () => { test("deepMerge({}, {a: 1}, {b: 2}) should return {a: 1, b: 2}", () => { @@ -20,7 +20,11 @@ describe("deepMerge", () => { }), ).toBeTruthy() }) - test("deepMerge([1,2,3], [2,3,4]) should equal [2,3,4]", () => { - expect(deepMerge([1, 2, 3], [2, 3, 4])).toEqual([2, 3, 4]) + test("Test that merging lists has Set semantics", () => { + const merge = deepMerge(["a", "b"], ["b", "c"]) + expect(merge).toHaveLength(3) + expect(merge).toContain("a") + expect(merge).toContain("b") + expect(merge).toContain("c") }) }) diff --git a/sdk/lib/test/util.getNetworkInterface.test.ts b/sdk/base/lib/test/util.getNetworkInterface.test.ts similarity index 100% rename from sdk/lib/test/util.getNetworkInterface.test.ts rename to sdk/base/lib/test/util.getNetworkInterface.test.ts diff --git a/sdk/base/lib/types.ts b/sdk/base/lib/types.ts new file mode 100644 index 000000000..56e310efe --- /dev/null +++ b/sdk/base/lib/types.ts @@ -0,0 +1,297 @@ +export * as inputSpecTypes from "./actions/input/inputSpecTypes" + +import { + DependencyRequirement, + NamedHealthCheckResult, + Manifest, + ServiceInterface, + ActionId, +} from "./osBindings" +import { Affine, StringObject, ToKebab } from "./util" +import { Action, Actions } from "./actions/setupActions" +import { Effects } from "./Effects" +export { Effects } +export * from "./osBindings" +export { SDKManifest } from "./types/ManifestTypes" + +export type ExposedStorePaths = string[] & Affine<"ExposedStorePaths"> +declare const HealthProof: unique symbol +export type HealthReceipt = { + [HealthProof]: never +} + +export type DaemonBuildable = { + build(): Promise<{ + term(): Promise + }> +} + +export type ServiceInterfaceType = "ui" | "p2p" | "api" +export type Signals = NodeJS.Signals +export const SIGTERM: Signals = "SIGTERM" +export const SIGKILL: Signals = "SIGKILL" +export const NO_TIMEOUT = -1 + +export type PathMaker = (options: { volume: string; path: string }) => string +export type ExportedAction = (options: { + effects: Effects + input?: Record +}) => Promise +export type MaybePromise = Promise | A +export namespace ExpectedExports { + version: 1 + + /** For backing up service data though the startOS UI */ + export type createBackup = (options: { effects: Effects }) => Promise + /** For restoring service data that was previously backed up using the startOS UI create backup flow. Backup restores are also triggered via the startOS UI, or doing a system restore flow during setup. */ + export type restoreBackup = (options: { + effects: Effects + }) => Promise + + /** + * This is the entrypoint for the main container. Used to start up something like the service that the + * package represents, like running a bitcoind in a bitcoind-wrapper. + */ + export type main = (options: { + effects: Effects + started(onTerm: () => PromiseLike): PromiseLike + }) => Promise + + /** + * After a shutdown, if we wanted to do any operations to clean up things, like + * set the action as unavailable or something. + */ + export type afterShutdown = (options: { + effects: Effects + }) => Promise + + /** + * Every time a service launches (both on startup, and on install) this function is called before packageInit + * Can be used to register callbacks + */ + export type containerInit = (options: { + effects: Effects + }) => Promise + + /** + * Every time a package completes an install, this function is called before the main. + * Can be used to do migration like things. + */ + export type packageInit = (options: { effects: Effects }) => Promise + /** This will be ran during any time a package is uninstalled, for example during a update + * this will be called. + */ + export type packageUninit = (options: { + effects: Effects + nextVersion: null | string + }) => Promise + + export type properties = (options: { + effects: Effects + }) => Promise + + export type manifest = Manifest + + export type actions = Actions< + any, + Record> + > +} +export type ABI = { + createBackup: ExpectedExports.createBackup + restoreBackup: ExpectedExports.restoreBackup + main: ExpectedExports.main + afterShutdown: ExpectedExports.afterShutdown + containerInit: ExpectedExports.containerInit + packageInit: ExpectedExports.packageInit + packageUninit: ExpectedExports.packageUninit + properties: ExpectedExports.properties + manifest: ExpectedExports.manifest + actions: ExpectedExports.actions +} +export type TimeMs = number +export type VersionString = string + +declare const DaemonProof: unique symbol +export type DaemonReceipt = { + [DaemonProof]: never +} +export type Daemon = { + wait(): Promise + term(): Promise + [DaemonProof]: never +} + +export type HealthStatus = NamedHealthCheckResult["result"] +export type SmtpValue = { + server: string + port: number + from: string + login: string + password: string | null | undefined +} + +export type CommandType = string | [string, ...string[]] + +export type DaemonReturned = { + wait(): Promise + term(options?: { signal?: Signals; timeout?: number }): Promise +} + +export declare const hostName: unique symbol +// asdflkjadsf.onion | 1.2.3.4 +export type Hostname = string & { [hostName]: never } + +export type HostnameInfoIp = { + kind: "ip" + networkInterfaceId: string + public: boolean + hostname: + | { + kind: "ipv4" | "ipv6" | "local" + value: string + port: number | null + sslPort: number | null + } + | { + kind: "domain" + domain: string + subdomain: string | null + port: number | null + sslPort: number | null + } +} + +export type HostnameInfoOnion = { + kind: "onion" + hostname: { value: string; port: number | null; sslPort: number | null } +} + +export type HostnameInfo = HostnameInfoIp | HostnameInfoOnion + +export type ServiceInterfaceId = string + +export { ServiceInterface } +export type ExposeServicePaths = { + /** The path to the value in the Store. [JsonPath](https://jsonpath.com/) */ + paths: ExposedStorePaths +} + +export type SdkPropertiesValue = + | { + type: "object" + value: { [k: string]: SdkPropertiesValue } + description?: string + } + | { + type: "string" + /** The value to display to the user */ + value: string + /** A human readable description or explanation of the value */ + description?: string + /** Whether or not to mask the value, for example, when displaying a password */ + masked?: boolean + /** Whether or not to include a button for copying the value to clipboard */ + copyable?: boolean + /** Whether or not to include a button for displaying the value as a QR code */ + qr?: boolean + } + +export type SdkPropertiesReturn = { + [key: string]: SdkPropertiesValue +} + +export type PropertiesValue = + | { + /** The type of this value, either "string" or "object" */ + type: "object" + /** A nested mapping of values. The user will experience this as a nested page with back button */ + value: { [k: string]: PropertiesValue } + /** (optional) A human readable description of the new set of values */ + description: string | null + } + | { + /** The type of this value, either "string" or "object" */ + type: "string" + /** The value to display to the user */ + value: string + /** A human readable description of the value */ + description: string | null + /** Whether or not to mask the value, for example, when displaying a password */ + masked: boolean | null + /** Whether or not to include a button for copying the value to clipboard */ + copyable: boolean | null + /** Whether or not to include a button for displaying the value as a QR code */ + qr: boolean | null + } + +export type PropertiesReturn = { + [key: string]: PropertiesValue +} + +export type EffectMethod = { + [K in keyof T]-?: K extends string + ? T[K] extends Function + ? ToKebab + : T[K] extends StringObject + ? `${ToKebab}.${EffectMethod}` + : never + : never +}[keyof T] + +export type SyncOptions = { + /** delete files that exist in the target directory, but not in the source directory */ + delete: boolean + /** do not sync files with paths that match these patterns */ + exclude: string[] +} + +/** + * This is the metadata that is returned from the metadata call. + */ +export type Metadata = { + fileType: string + isDir: boolean + isFile: boolean + isSymlink: boolean + len: number + modified?: Date + accessed?: Date + created?: Date + readonly: boolean + uid: number + gid: number + mode: number +} + +export type ActionResult = { + version: "0" + message: string + value: string | null + copyable: boolean + qr: boolean +} +export type SetResult = { + dependsOn: DependsOn + signal: Signals +} + +export type PackageId = string +export type Message = string +export type DependencyKind = "running" | "exists" + +export type DependsOn = { + [packageId: string]: string[] | readonly string[] +} + +export type KnownError = + | { error: string } + | { + errorCode: [number, string] | readonly [number, string] + } + +export type Dependencies = Array + +export type DeepPartial = T extends {} + ? { [P in keyof T]?: DeepPartial } + : T diff --git a/sdk/base/lib/types/ManifestTypes.ts b/sdk/base/lib/types/ManifestTypes.ts new file mode 100644 index 000000000..c416588b3 --- /dev/null +++ b/sdk/base/lib/types/ManifestTypes.ts @@ -0,0 +1,159 @@ +import { T } from ".." +import { ImageId, ImageSource } from "../types" + +export type SDKManifest = { + /** + * The package identifier used by StartOS. This must be unique amongst all other known packages. + * @example nextcloud + * */ + readonly id: string + /** + * The human readable name of your service + * @example Nextcloud + * */ + readonly title: string + /** + * The name of the software license for this project. The license itself should be included in the root of the project directory. + * @example MIT + */ + readonly license: string + /** + * URL of the StartOS package repository + * @example `https://github.com/Start9Labs/nextcloud-startos` + */ + readonly wrapperRepo: string + /** + * URL of the upstream service repository + * @example `https://github.com/nextcloud/docker` + */ + readonly upstreamRepo: string + /** + * URL where users can get help using the upstream service + * @example `https://github.com/nextcloud/docker/issues` + */ + readonly supportSite: string + /** + * URL where users can learn more about the upstream service + * @example `https://nextcloud.com` + */ + readonly marketingSite: string + /** + * (optional) URL where users can donate to the upstream project + * @example `https://nextcloud.com/contribute/` + */ + readonly donationUrl: string | null + readonly description: { + /** Short description to display on the marketplace list page. Max length 80 chars. */ + readonly short: string + /** Long description to display on the marketplace details page for this service. Max length 500 chars. */ + readonly long: string + } + /** + * @description A mapping of OS images needed to run the container processes. Each image ID is a unique key. + * @example + * Using dockerTag... + * + * ``` + images: { + main: { + source: { + dockerTag: 'start9/hello-world', + }, + }, + }, + * ``` + * @example + * Using dockerBuild... + * + * ``` + images: { + main: { + source: { + dockerBuild: { + dockerFile: '../Dockerfile', + workdir: '.', + }, + }, + }, + }, + * ``` + */ + readonly images: Record + /** + * @description A list of readonly asset directories that will mount to the container. Each item here must + * correspond to a directory in the /assets directory of this project. + * + * Most projects will not make use of this. + * @example [] + */ + readonly assets: string[] + /** + * @description A list of data volumes that will mount to the container. Must contain at least one volume. + * @example ['main'] + */ + readonly volumes: string[] + + readonly alerts?: { + /** An warning alert requiring user confirmation before proceeding with initial installation of this service. */ + readonly install?: string | null + /** An warning alert requiring user confirmation before updating this service. */ + readonly update?: string | null + /** An warning alert requiring user confirmation before uninstalling this service. */ + readonly uninstall?: string | null + /** An warning alert requiring user confirmation before restoring this service from backup. */ + readonly restore?: string | null + /** An warning alert requiring user confirmation before starting this service. */ + readonly start?: string | null + /** An warning alert requiring user confirmation before stopping this service. */ + readonly stop?: string | null + } + /** + * @description A mapping of service dependencies to be displayed to users when viewing the Marketplace + * @property {string} description - An explanation of why this service is a dependency. + * @property {boolean} optional - Whether or not this dependency is required or contingent on user configuration. + * @property {string} s9pk - A path or url to an s9pk of the dependency to extract metadata at build time + * @example + * ``` + dependencies: { + 'hello-world': { + description: 'A moon needs a world', + optional: false, + s9pk: '', + }, + }, + * ``` + */ + readonly dependencies: Record + /** + * @description (optional) A set of hardware requirements for this service. If the user's machine + * does not meet these requirements, they will not be able to install this service. + * @property {object[]} devices - TODO Aiden confirm type on the left. List of required devices (displays or processors). + * @property {number} ram - Minimum RAM requirement (in megabytes MB) + * @property {string[]} arch - List of supported arches + * @example + * ``` + TODO Aiden verify below and provide examples for devices + hardwareRequirements: { + devices: [ + { class: 'display', value: '' }, + { class: 'processor', value: '' }, + ], + ram: 8192, + arch: ['x86-64'], + }, + * ``` + */ + readonly hardwareRequirements?: { + readonly device?: { display?: RegExp; processor?: RegExp } + readonly ram?: number | null + readonly arch?: string[] | null + } +} + +export type SDKImageInputSpec = { + source: Exclude + arch?: string[] + emulateMissingAs?: string | null +} + +export type ManifestDependency = T.Manifest["dependencies"][string] diff --git a/sdk/lib/util/GetSystemSmtp.ts b/sdk/base/lib/util/GetSystemSmtp.ts similarity index 90% rename from sdk/lib/util/GetSystemSmtp.ts rename to sdk/base/lib/util/GetSystemSmtp.ts index 498a2d8b2..cd87a9db6 100644 --- a/sdk/lib/util/GetSystemSmtp.ts +++ b/sdk/base/lib/util/GetSystemSmtp.ts @@ -1,4 +1,4 @@ -import { Effects } from "../types" +import { Effects } from "../Effects" export class GetSystemSmtp { constructor(readonly effects: Effects) {} @@ -8,7 +8,7 @@ export class GetSystemSmtp { */ const() { return this.effects.getSystemSmtp({ - callback: this.effects.restart, + callback: () => this.effects.constRetry(), }) } /** diff --git a/sdk/lib/util/Hostname.ts b/sdk/base/lib/util/Hostname.ts similarity index 100% rename from sdk/lib/util/Hostname.ts rename to sdk/base/lib/util/Hostname.ts diff --git a/sdk/lib/store/PathBuilder.ts b/sdk/base/lib/util/PathBuilder.ts similarity index 100% rename from sdk/lib/store/PathBuilder.ts rename to sdk/base/lib/util/PathBuilder.ts diff --git a/sdk/base/lib/util/asError.ts b/sdk/base/lib/util/asError.ts new file mode 100644 index 000000000..c3454e0e4 --- /dev/null +++ b/sdk/base/lib/util/asError.ts @@ -0,0 +1,9 @@ +export const asError = (e: unknown) => { + if (e instanceof Error) { + return new Error(e as any) + } + if (typeof e === "string") { + return new Error(`${e}`) + } + return new Error(`${JSON.stringify(e)}`) +} diff --git a/sdk/lib/util/deepEqual.ts b/sdk/base/lib/util/deepEqual.ts similarity index 100% rename from sdk/lib/util/deepEqual.ts rename to sdk/base/lib/util/deepEqual.ts diff --git a/sdk/base/lib/util/deepMerge.ts b/sdk/base/lib/util/deepMerge.ts new file mode 100644 index 000000000..72392a887 --- /dev/null +++ b/sdk/base/lib/util/deepMerge.ts @@ -0,0 +1,86 @@ +export function partialDiff( + prev: T, + next: T, +): { diff: Partial } | undefined { + if (prev === next) { + return + } else if (Array.isArray(prev) && Array.isArray(next)) { + const res = { diff: [] as any[] } + for (let newItem of next) { + let anyEq = false + for (let oldItem of prev) { + if (!partialDiff(oldItem, newItem)) { + anyEq = true + break + } + } + if (!anyEq) { + res.diff.push(newItem) + } + } + if (res.diff.length) { + return res as any + } else { + return + } + } else if (typeof prev === "object" && typeof next === "object") { + if (prev === null) { + return { diff: next } + } + if (next === null) return + const res = { diff: {} as Record } + for (let key in next) { + const diff = partialDiff(prev[key], next[key]) + if (diff) { + res.diff[key] = diff.diff + } + } + if (Object.keys(res.diff).length) { + return res + } else { + return + } + } else { + return { diff: next } + } +} + +export function deepMerge(...args: unknown[]): unknown { + const lastItem = (args as any)[args.length - 1] + if (typeof lastItem !== "object" || !lastItem) return lastItem + if (Array.isArray(lastItem)) + return deepMergeList( + ...(args.filter((x) => Array.isArray(x)) as unknown[][]), + ) + return deepMergeObject( + ...(args.filter( + (x) => typeof x === "object" && x && !Array.isArray(x), + ) as object[]), + ) +} + +function deepMergeList(...args: unknown[][]): unknown[] { + const res: unknown[] = [] + for (let arg of args) { + for (let item of arg) { + if (!res.some((x) => !partialDiff(x, item))) { + res.push(item) + } + } + } + return res +} + +function deepMergeObject(...args: object[]): object { + const lastItem = (args as any)[args.length - 1] + if (args.length === 0) return lastItem as any + if (args.length === 1) args.unshift({}) + const allKeys = new Set(args.flatMap((x) => Object.keys(x))) + for (const key of allKeys) { + const filteredValues = args.flatMap((x) => + key in x ? [(x as any)[key]] : [], + ) + ;(args as any)[0][key] = deepMerge(...filteredValues) + } + return args[0] as any +} diff --git a/sdk/lib/util/getDefaultString.ts b/sdk/base/lib/util/getDefaultString.ts similarity index 79% rename from sdk/lib/util/getDefaultString.ts rename to sdk/base/lib/util/getDefaultString.ts index fa35b4e66..2bbf8d279 100644 --- a/sdk/lib/util/getDefaultString.ts +++ b/sdk/base/lib/util/getDefaultString.ts @@ -1,4 +1,4 @@ -import { DefaultString } from "../config/configTypes" +import { DefaultString } from "../actions/input/inputSpecTypes" import { getRandomString } from "./getRandomString" export function getDefaultString(defaultSpec: DefaultString): string { diff --git a/sdk/lib/util/getRandomCharInSet.ts b/sdk/base/lib/util/getRandomCharInSet.ts similarity index 100% rename from sdk/lib/util/getRandomCharInSet.ts rename to sdk/base/lib/util/getRandomCharInSet.ts diff --git a/sdk/lib/util/getRandomString.ts b/sdk/base/lib/util/getRandomString.ts similarity index 79% rename from sdk/lib/util/getRandomString.ts rename to sdk/base/lib/util/getRandomString.ts index ea0989bcd..7b52041d8 100644 --- a/sdk/lib/util/getRandomString.ts +++ b/sdk/base/lib/util/getRandomString.ts @@ -1,4 +1,4 @@ -import { RandomString } from "../config/configTypes" +import { RandomString } from "../actions/input/inputSpecTypes" import { getRandomCharInSet } from "./getRandomCharInSet" export function getRandomString(generator: RandomString): string { diff --git a/sdk/lib/util/getServiceInterface.ts b/sdk/base/lib/util/getServiceInterface.ts similarity index 98% rename from sdk/lib/util/getServiceInterface.ts rename to sdk/base/lib/util/getServiceInterface.ts index fd0fef779..cbbb345cb 100644 --- a/sdk/lib/util/getServiceInterface.ts +++ b/sdk/base/lib/util/getServiceInterface.ts @@ -1,8 +1,7 @@ -import { ServiceInterfaceType } from "../StartSdk" +import { ServiceInterfaceType } from "../types" import { knownProtocols } from "../interfaces/Host" import { AddressInfo, - Effects, Host, HostAddress, Hostname, @@ -11,6 +10,7 @@ import { HostnameInfoOnion, IpInfo, } from "../types" +import { Effects } from "../Effects" export type UrlString = string export type HostId = string @@ -232,7 +232,7 @@ export class GetServiceInterface { */ async const() { const { id, packageId } = this.opts - const callback = this.effects.restart + const callback = () => this.effects.constRetry() const interfaceFilled = await makeInterfaceFilled({ effects: this.effects, id, diff --git a/sdk/lib/util/getServiceInterfaces.ts b/sdk/base/lib/util/getServiceInterfaces.ts similarity index 96% rename from sdk/lib/util/getServiceInterfaces.ts rename to sdk/base/lib/util/getServiceInterfaces.ts index 9f0e242b8..1d83684d6 100644 --- a/sdk/lib/util/getServiceInterfaces.ts +++ b/sdk/base/lib/util/getServiceInterfaces.ts @@ -1,4 +1,4 @@ -import { Effects } from "../types" +import { Effects } from "../Effects" import { ServiceInterfaceFilled, filledAddress, @@ -63,7 +63,7 @@ export class GetServiceInterfaces { */ async const() { const { packageId } = this.opts - const callback = this.effects.restart + const callback = () => this.effects.constRetry() const interfaceFilled: ServiceInterfaceFilled[] = await makeManyInterfaceFilled({ effects: this.effects, diff --git a/sdk/lib/util/graph.ts b/sdk/base/lib/util/graph.ts similarity index 100% rename from sdk/lib/util/graph.ts rename to sdk/base/lib/util/graph.ts diff --git a/sdk/lib/util/inMs.test.ts b/sdk/base/lib/util/inMs.test.ts similarity index 100% rename from sdk/lib/util/inMs.test.ts rename to sdk/base/lib/util/inMs.test.ts diff --git a/sdk/lib/util/inMs.ts b/sdk/base/lib/util/inMs.ts similarity index 95% rename from sdk/lib/util/inMs.ts rename to sdk/base/lib/util/inMs.ts index 547fb8bea..548eb14bf 100644 --- a/sdk/lib/util/inMs.ts +++ b/sdk/base/lib/util/inMs.ts @@ -1,5 +1,3 @@ -import { DEFAULT_SIGTERM_TIMEOUT } from "../mainFn" - const matchTimeRegex = /^\s*(\d+)?(\.\d+)?\s*(ms|s|m|h|d)/ const unitMultiplier = (unit?: string) => { diff --git a/sdk/base/lib/util/index.ts b/sdk/base/lib/util/index.ts new file mode 100644 index 000000000..4c9e803bb --- /dev/null +++ b/sdk/base/lib/util/index.ts @@ -0,0 +1,22 @@ +/// Currently being used +export { addressHostToUrl } from "./getServiceInterface" +export { getDefaultString } from "./getDefaultString" + +/// Not being used, but known to be browser compatible +export { GetServiceInterface, getServiceInterface } from "./getServiceInterface" +export { getServiceInterfaces } from "./getServiceInterfaces" +export { once } from "./once" +export { asError } from "./asError" +export * as Patterns from "./patterns" +export * from "./typeHelpers" +export { GetSystemSmtp } from "./GetSystemSmtp" +export { Graph, Vertex } from "./graph" +export { inMs } from "./inMs" +export { splitCommand } from "./splitCommand" +export { nullIfEmpty } from "./nullIfEmpty" +export { deepMerge, partialDiff } from "./deepMerge" +export { deepEqual } from "./deepEqual" +export { hostnameInfoToAddress } from "./Hostname" +export { PathBuilder, extractJsonPath, StorePath } from "./PathBuilder" +export * as regexes from "./regexes" +export { stringFromStdErrOut } from "./stringFromStdErrOut" diff --git a/sdk/base/lib/util/nullIfEmpty.ts b/sdk/base/lib/util/nullIfEmpty.ts new file mode 100644 index 000000000..b24907b7d --- /dev/null +++ b/sdk/base/lib/util/nullIfEmpty.ts @@ -0,0 +1,10 @@ +/** + * A useful tool when doing a getInputSpec. + * Look into the inputSpec {@link FileHelper} for an example of the use. + * @param s + * @returns + */ +export function nullIfEmpty>(s: null | A) { + if (s === null) return null + return Object.keys(s).length === 0 ? null : s +} diff --git a/sdk/lib/util/once.ts b/sdk/base/lib/util/once.ts similarity index 100% rename from sdk/lib/util/once.ts rename to sdk/base/lib/util/once.ts diff --git a/sdk/lib/util/patterns.ts b/sdk/base/lib/util/patterns.ts similarity index 96% rename from sdk/lib/util/patterns.ts rename to sdk/base/lib/util/patterns.ts index ac281b081..2c9c7010d 100644 --- a/sdk/lib/util/patterns.ts +++ b/sdk/base/lib/util/patterns.ts @@ -1,4 +1,4 @@ -import { Pattern } from "../config/configTypes" +import { Pattern } from "../actions/input/inputSpecTypes" import * as regexes from "./regexes" export const ipv6: Pattern = { diff --git a/sdk/lib/util/regexes.ts b/sdk/base/lib/util/regexes.ts similarity index 100% rename from sdk/lib/util/regexes.ts rename to sdk/base/lib/util/regexes.ts diff --git a/sdk/lib/util/splitCommand.ts b/sdk/base/lib/util/splitCommand.ts similarity index 100% rename from sdk/lib/util/splitCommand.ts rename to sdk/base/lib/util/splitCommand.ts diff --git a/sdk/lib/util/stringFromStdErrOut.ts b/sdk/base/lib/util/stringFromStdErrOut.ts similarity index 100% rename from sdk/lib/util/stringFromStdErrOut.ts rename to sdk/base/lib/util/stringFromStdErrOut.ts diff --git a/sdk/lib/util/typeHelpers.ts b/sdk/base/lib/util/typeHelpers.ts similarity index 100% rename from sdk/lib/util/typeHelpers.ts rename to sdk/base/lib/util/typeHelpers.ts diff --git a/sdk/base/package-lock.json b/sdk/base/package-lock.json new file mode 100644 index 000000000..3a8e40c0c --- /dev/null +++ b/sdk/base/package-lock.json @@ -0,0 +1,4677 @@ +{ + "name": "@start9labs/start-sdk-base", + "version": "0.3.6-alpha8", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@start9labs/start-sdk-base", + "license": "MIT", + "dependencies": { + "@iarna/toml": "^2.2.5", + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0", + "isomorphic-fetch": "^3.0.0", + "lodash.merge": "^4.6.2", + "mime": "^4.0.3", + "ts-matches": "^5.5.1", + "yaml": "^2.2.2" + }, + "devDependencies": { + "@types/jest": "^29.4.0", + "@types/lodash.merge": "^4.6.2", + "jest": "^29.4.3", + "peggy": "^3.0.2", + "prettier": "^3.2.5", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.1", + "ts-pegjs": "^4.2.1", + "tsx": "^4.7.1", + "typescript": "^5.0.4" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", + "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.3.tgz", + "integrity": "sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.21.3", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.21.2", + "@babel/helpers": "^7.21.0", + "@babel/parser": "^7.21.3", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.3", + "@babel/types": "^7.21.3", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/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/@babel/generator": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.3.tgz", + "integrity": "sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.21.3", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "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/@babel/helper-compilation-targets": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", + "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.20.5", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", + "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.2", + "@babel/types": "^7.21.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", + "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/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/@babel/highlight/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/@babel/highlight/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/@babel/highlight/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/@babel/highlight/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/@babel/highlight/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/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz", + "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.3.tgz", + "integrity": "sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.21.3", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.21.3", + "@babel/types": "^7.21.3", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz", + "integrity": "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@iarna/toml": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", + "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" + }, + "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==", + "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" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.5.0.tgz", + "integrity": "sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.5.0.tgz", + "integrity": "sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.5.0", + "@jest/reporters": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@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.5.0", + "jest-config": "^29.5.0", + "jest-haste-map": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-resolve-dependencies": "^29.5.0", + "jest-runner": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "jest-watcher": "^29.5.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "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/environment": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz", + "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-mock": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==", + "dev": true, + "dependencies": { + "expect": "^29.5.0", + "jest-snapshot": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", + "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.4.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz", + "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.5.0", + "jest-mock": "^29.5.0", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.5.0.tgz", + "integrity": "sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.5.0", + "@jest/expect": "^29.5.0", + "@jest/types": "^29.5.0", + "jest-mock": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.5.0.tgz", + "integrity": "sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@jridgewell/trace-mapping": "^0.3.15", + "@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": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "jest-worker": "^29.5.0", + "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" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", + "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.25.16" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.3.tgz", + "integrity": "sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.15", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.5.0.tgz", + "integrity": "sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz", + "integrity": "sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.5.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.5.0.tgz", + "integrity": "sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.5.0", + "@jridgewell/trace-mapping": "^0.3.15", + "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.5.0", + "jest-regex-util": "^29.4.3", + "jest-util": "^29.5.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", + "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.4.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/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "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.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@noble/curves": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.0.tgz", + "integrity": "sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "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": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.25.24", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", + "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0" + } + }, + "node_modules/@ts-morph/common": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.19.0.tgz", + "integrity": "sha512-Unz/WHmd4pGax91rdIKWi51wnVUW11QttMEPpBiBgIewnc9UQIX7UDLxr5vRlqeByXCwhkF6VabSsI0raWcyAQ==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.12", + "minimatch": "^7.4.3", + "mkdirp": "^2.1.6", + "path-browserify": "^1.0.1" + } + }, + "node_modules/@ts-morph/common/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@ts-morph/common/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==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", + "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", + "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.18.3", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", + "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "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.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.0.tgz", + "integrity": "sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/lodash": { + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.5.tgz", + "integrity": "sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==", + "dev": true + }, + "node_modules/@types/lodash.merge": { + "version": "4.6.9", + "resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.9.tgz", + "integrity": "sha512-23sHDPmzd59kUgWyKGiOMO2Qb9YtqRO/x4IhkgNUiPQ1+5MUVqi6bCZeq9nBJ17msjIMbEIO5u+XW4Kz6aGUhQ==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/node": { + "version": "18.15.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.10.tgz", + "integrity": "sha512-9avDaQJczATcXgfmMAW3MIWArOO7A+m90vuCFLr8AotWf8igO/mRoYukrk2cqZVtv38tHs33retzHEilM7FpeQ==", + "dev": true + }, + "node_modules/@types/prettier": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", + "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", + "dev": true + }, + "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/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/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "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/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-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/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/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "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/babel-jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz", + "integrity": "sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.5.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.5.0", + "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-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.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", + "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", + "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.5.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", + "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.5.0", + "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==", + "dev": true + }, + "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/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.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" + }, + "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/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.30001470", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001470.tgz", + "integrity": "sha512-065uNwY6QtHCBOExzbV6m236DDhYCCtPmQUCoQtwkVqzud8v5QPidoMr6CoMkC2nfp6nksjttqWQRRh75LqUmA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "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/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.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true + }, + "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/cliui/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/cliui/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/cliui/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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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/code-block-writer": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz", + "integrity": "sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==", + "dev": true + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "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/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/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "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": "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/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "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": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "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/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": 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/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.4.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", + "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.341", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.341.tgz", + "integrity": "sha512-R4A8VfUBQY9WmAhuqY5tjHRf5fH2AAf6vqitBOE0y6u2PgHgqHSrhZmu78dIX3fVZtjqlwJNX1i2zwC3VpHtQQ==", + "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/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/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": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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/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/expect": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/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/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "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/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-tsconfig": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", + "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "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-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": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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": "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/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/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/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-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-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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-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": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "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/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isomorphic-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", + "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", + "dependencies": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, + "node_modules/isomorphic-fetch/node_modules/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "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.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.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.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", + "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==", + "dev": true, + "dependencies": { + "@jest/core": "^29.5.0", + "@jest/types": "^29.5.0", + "import-local": "^3.0.2", + "jest-cli": "^29.5.0" + }, + "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.5.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", + "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.5.0.tgz", + "integrity": "sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.5.0", + "@jest/expect": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.5.0", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.5.0", + "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-cli": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.5.0.tgz", + "integrity": "sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "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-config": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.5.0.tgz", + "integrity": "sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.5.0", + "@jest/types": "^29.5.0", + "babel-jest": "^29.5.0", + "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.5.0", + "jest-environment-node": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-runner": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.5.0", + "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-diff": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", + "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.4.3", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", + "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", + "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.5.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.5.0.tgz", + "integrity": "sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.3", + "jest-util": "^29.5.0", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz", + "integrity": "sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.5.0", + "@jest/fake-timers": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-mock": "^29.5.0", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", + "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.5.0.tgz", + "integrity": "sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@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.4.3", + "jest-util": "^29.5.0", + "jest-worker": "^29.5.0", + "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.5.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz", + "integrity": "sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", + "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.5.0", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", + "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.5.0", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz", + "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-util": "^29.5.0" + }, + "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.4.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", + "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.5.0.tgz", + "integrity": "sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "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.5.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz", + "integrity": "sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.4.3", + "jest-snapshot": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.5.0.tgz", + "integrity": "sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.5.0", + "@jest/environment": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.4.3", + "jest-environment-node": "^29.5.0", + "jest-haste-map": "^29.5.0", + "jest-leak-detector": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-resolve": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-util": "^29.5.0", + "jest-watcher": "^29.5.0", + "jest-worker": "^29.5.0", + "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-runtime": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.5.0.tgz", + "integrity": "sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.5.0", + "@jest/fake-timers": "^29.5.0", + "@jest/globals": "^29.5.0", + "@jest/source-map": "^29.4.3", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@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.5.0", + "jest-message-util": "^29.5.0", + "jest-mock": "^29.5.0", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.5.0.tgz", + "integrity": "sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==", + "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/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.5.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.5.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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.5.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", + "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@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-validate": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.5.0.tgz", + "integrity": "sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.3", + "leven": "^3.1.0", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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-watcher": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.5.0.tgz", + "integrity": "sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.5.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz", + "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.5.0", + "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/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/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/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/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==" + }, + "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/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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/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": ">= 8" + } + }, + "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": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.3.tgz", + "integrity": "sha512-KgUb15Oorc0NEKPbvfa0wRU+PItIEZmiv+pyAO2i0oTIVTJhlzMclU7w4RXWQrSOVH5ax/p/CkIO7KI4OyFJTQ==", + "funding": [ + "https://github.com/sponsors/broofa" + ], + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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/mkdirp": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", + "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", + "dev": true, + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/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/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.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "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==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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==", + "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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, + "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==", + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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/peggy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/peggy/-/peggy-3.0.2.tgz", + "integrity": "sha512-n7chtCbEoGYRwZZ0i/O3t1cPr6o+d9Xx4Zwy2LYfzv0vjchMBU0tO+qYYyvZloBPcgRgzYvALzGWHe609JjEpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^10.0.0", + "source-map-generator": "0.8.0" + }, + "bin": { + "peggy": "bin/peggy.js" + }, + "engines": { + "node": ">=14" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "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==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", + "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.4.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "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_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pure-rand": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.1.tgz", + "integrity": "sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "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/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 + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "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, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "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": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "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": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "engines": { + "node": ">=8" + } + }, + "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/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-generator": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/source-map-generator/-/source-map-generator-0.8.0.tgz", + "integrity": "sha512-psgxdGMwl5MZM9S3FWee4EgsEaIjahYV5AzGnwUvPhWeITz/j6rKpysQHlQ4USdxvINlb8lKfWGIXwfkrgtqkA==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "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/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/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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": ">=8" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "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-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": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/ts-jest": { + "version": "29.0.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.5.tgz", + "integrity": "sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA==", + "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.x", + "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" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "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==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "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/ts-matches": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-5.5.1.tgz", + "integrity": "sha512-UFYaKgfqlg9FROK7bdpYqFwG1CJvP4kOJdjXuWoqxo9jCmANoDw1GxkSCpJgoTeIiSTaTH5Qr1klSspb8c+ydg==" + }, + "node_modules/ts-morph": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-18.0.0.tgz", + "integrity": "sha512-Kg5u0mk19PIIe4islUI/HWRvm9bC1lHejK4S0oh1zaZ77TMZAEmQC0sHQYiu2RgCQFZKXz1fMVi/7nOOeirznA==", + "dev": true, + "dependencies": { + "@ts-morph/common": "~0.19.0", + "code-block-writer": "^12.0.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-pegjs": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ts-pegjs/-/ts-pegjs-4.2.1.tgz", + "integrity": "sha512-mK/O2pu6lzWUeKpEMA/wsa0GdYblfjJI1y0s0GqH6xCTvugQDOWPJbm5rY6AHivpZICuXIriCb+a7Cflbdtc2w==", + "dev": true, + "dependencies": { + "prettier": "^2.8.8", + "ts-morph": "^18.0.0" + }, + "bin": { + "tspegjs": "dist/cli.mjs" + }, + "peerDependencies": { + "peggy": "^3.0.2" + } + }, + "node_modules/ts-pegjs/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": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/tsx": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.7.1.tgz", + "integrity": "sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==", + "dev": true, + "dependencies": { + "esbuild": "~0.19.10", + "get-tsconfig": "^4.7.2" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "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==", + "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" + } + }, + "node_modules/v8-to-istanbul/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/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": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-fetch": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", + "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" + }, + "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_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" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "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==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/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/wrap-ansi/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/wrap-ansi/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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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/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/yaml": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", + "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "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/yargs/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/yargs/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/yargs/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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/sdk/base/package.json b/sdk/base/package.json new file mode 100644 index 000000000..a290d2a75 --- /dev/null +++ b/sdk/base/package.json @@ -0,0 +1,51 @@ +{ + "name": "@start9labs/start-sdk-base", + "main": "./index.js", + "types": "./index.d.ts", + "sideEffects": true, + "scripts": { + "peggy": "peggy --allowed-start-rules '*' --plugin ./node_modules/ts-pegjs/dist/tspegjs -o lib/exver/exver.ts lib/exver/exver.pegjs", + "test": "jest -c ./jest.config.js --coverage", + "buildOutput": "npx prettier --write '**/*.ts'", + "check": "tsc --noEmit", + "tsc": "tsc" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Start9Labs/start-sdk.git" + }, + "author": "Start9 Labs", + "license": "MIT", + "bugs": { + "url": "https://github.com/Start9Labs/start-sdk/issues" + }, + "homepage": "https://github.com/Start9Labs/start-sdk#readme", + "dependencies": { + "isomorphic-fetch": "^3.0.0", + "lodash.merge": "^4.6.2", + "mime": "^4.0.3", + "ts-matches": "^5.5.1", + "yaml": "^2.2.2", + "@iarna/toml": "^2.2.5", + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0" + }, + "prettier": { + "trailingComma": "all", + "tabWidth": 2, + "semi": false, + "singleQuote": false + }, + "devDependencies": { + "@types/jest": "^29.4.0", + "@types/lodash.merge": "^4.6.2", + "jest": "^29.4.3", + "peggy": "^3.0.2", + "prettier": "^3.2.5", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.1", + "ts-pegjs": "^4.2.1", + "tsx": "^4.7.1", + "typescript": "^5.0.4" + } +} diff --git a/sdk/tsconfig-base.json b/sdk/base/tsconfig.json similarity index 78% rename from sdk/tsconfig-base.json rename to sdk/base/tsconfig.json index cc14a817c..cd73f3164 100644 --- a/sdk/tsconfig-base.json +++ b/sdk/base/tsconfig.json @@ -1,18 +1,18 @@ { "compilerOptions": { - "module": "esnext", "strict": true, - "outDir": "dist", "preserveConstEnums": true, "sourceMap": true, - "target": "es2017", "pretty": true, "declaration": true, "noImplicitAny": true, "esModuleInterop": true, "types": ["node", "jest"], "moduleResolution": "node", - "skipLibCheck": true + "skipLibCheck": true, + "module": "commonjs", + "outDir": "../baseDist", + "target": "es2018" }, "include": ["lib/**/*"], "exclude": ["lib/**/*.spec.ts", "lib/**/*.gen.ts", "list", "node_modules"] diff --git a/sdk/lib/Dependency.ts b/sdk/lib/Dependency.ts deleted file mode 100644 index 067ed653e..000000000 --- a/sdk/lib/Dependency.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { VersionRange } from "./exver" - -export class Dependency { - constructor( - readonly data: - | { - type: "running" - versionRange: VersionRange - registryUrl: string - healthChecks: string[] - } - | { - type: "exists" - versionRange: VersionRange - registryUrl: string - }, - ) {} -} diff --git a/sdk/lib/StartSdk.ts b/sdk/lib/StartSdk.ts deleted file mode 100644 index 87c4e14ce..000000000 --- a/sdk/lib/StartSdk.ts +++ /dev/null @@ -1,805 +0,0 @@ -import { RequiredDefault, Value } from "./config/builder/value" -import { Config, ExtractConfigType, LazyBuild } from "./config/builder/config" -import { - DefaultString, - ListValueSpecText, - Pattern, - RandomString, - UniqueBy, - ValueSpecDatetime, - ValueSpecText, -} from "./config/configTypes" -import { Variants } from "./config/builder/variants" -import { CreatedAction, createAction } from "./actions/createAction" -import { - ActionMetadata, - Effects, - ActionResult, - BackupOptions, - DeepPartial, - MaybePromise, - ServiceInterfaceId, - PackageId, -} from "./types" -import * as patterns from "./util/patterns" -import { DependencyConfig, Update } from "./dependencies/DependencyConfig" -import { BackupSet, Backups } from "./backup/Backups" -import { smtpConfig } from "./config/configConstants" -import { Daemons } from "./mainFn/Daemons" -import { healthCheck, HealthCheckParams } from "./health/HealthCheck" -import { checkPortListening } from "./health/checkFns/checkPortListening" -import { checkWebUrl, runHealthScript } from "./health/checkFns" -import { List } from "./config/builder/list" -import { Install, InstallFn } from "./inits/setupInstall" -import { setupActions } from "./actions/setupActions" -import { setupDependencyConfig } from "./dependencies/setupDependencyConfig" -import { SetupBackupsParams, setupBackups } from "./backup/setupBackups" -import { setupInit } from "./inits/setupInit" -import { Uninstall, UninstallFn, setupUninstall } from "./inits/setupUninstall" -import { setupMain } from "./mainFn" -import { defaultTrigger } from "./trigger/defaultTrigger" -import { changeOnFirstSuccess, cooldownTrigger } from "./trigger" -import setupConfig, { - DependenciesReceipt, - Read, - Save, -} from "./config/setupConfig" -import { - InterfacesReceipt, - SetInterfaces, - setupInterfaces, -} from "./interfaces/setupInterfaces" -import { successFailure } from "./trigger/successFailure" -import { HealthReceipt } from "./health/HealthReceipt" -import { MultiHost, Scheme } from "./interfaces/Host" -import { ServiceInterfaceBuilder } from "./interfaces/ServiceInterfaceBuilder" -import { GetSystemSmtp } from "./util/GetSystemSmtp" -import nullIfEmpty from "./util/nullIfEmpty" -import { - GetServiceInterface, - getServiceInterface, -} from "./util/getServiceInterface" -import { getServiceInterfaces } from "./util/getServiceInterfaces" -import { getStore } from "./store/getStore" -import { CommandOptions, MountOptions, SubContainer } from "./util/SubContainer" -import { splitCommand } from "./util/splitCommand" -import { Mounts } from "./mainFn/Mounts" -import { Dependency } from "./Dependency" -import * as T from "./types" -import { testTypeVersion, ValidateExVer } from "./exver" -import { ExposedStorePaths } from "./store/setupExposeStore" -import { PathBuilder, extractJsonPath, pathBuilder } from "./store/PathBuilder" -import { - CheckDependencies, - checkDependencies, -} from "./dependencies/dependencies" -import { health } from "." -import { GetSslCertificate } from "./util/GetSslCertificate" -import { VersionGraph } from "./version" - -export const SDKVersion = testTypeVersion("0.3.6") - -// prettier-ignore -type AnyNeverCond = - T extends [] ? Else : - T extends [never, ...Array] ? Then : - T extends [any, ...infer U] ? AnyNeverCond : - never - -export type ServiceInterfaceType = "ui" | "p2p" | "api" -export type MainEffects = Effects & { - _type: "main" - clearCallbacks: () => Promise -} -export type Signals = NodeJS.Signals -export const SIGTERM: Signals = "SIGTERM" -export const SIGKILL: Signals = "SIGKILL" -export const NO_TIMEOUT = -1 - -function removeCallbackTypes(effects: E) { - return (t: T) => { - if ("_type" in effects && effects._type === "main") { - return t as E extends MainEffects ? T : Omit - } else { - if ("const" in t) { - delete t.const - } - if ("watch" in t) { - delete t.watch - } - return t as E extends MainEffects ? T : Omit - } - } -} - -export class StartSdk { - private constructor(readonly manifest: Manifest) {} - static of() { - return new StartSdk(null as never) - } - withManifest(manifest: Manifest) { - return new StartSdk(manifest) - } - withStore>() { - return new StartSdk(this.manifest) - } - - build(isReady: AnyNeverCond<[Manifest, Store], "Build not ready", true>) { - type DependencyType = { - [K in keyof { - [K in keyof Manifest["dependencies"]]: Manifest["dependencies"][K]["optional"] extends false - ? K - : never - }]: Dependency - } & { - [K in keyof { - [K in keyof Manifest["dependencies"]]: Manifest["dependencies"][K]["optional"] extends true - ? K - : never - }]?: Dependency - } - - type NestedEffects = "subcontainer" | "store" - type InterfaceEffects = - | "getServiceInterface" - | "listServiceInterfaces" - | "exportServiceInterface" - | "clearServiceInterfaces" - | "bind" - | "getHostInfo" - | "getPrimaryUrl" - type MainUsedEffects = "setMainStatus" | "setHealth" - type AlreadyExposed = "getSslCertificate" | "getSystemSmtp" - - // prettier-ignore - type StartSdkEffectWrapper = { - [K in keyof Omit]: (effects: Effects, ...args: Parameters) => ReturnType - } - const startSdkEffectWrapper: StartSdkEffectWrapper = { - executeAction: (effects, ...args) => effects.executeAction(...args), - exportAction: (effects, ...args) => effects.exportAction(...args), - clearActions: (effects, ...args) => effects.clearActions(...args), - getConfigured: (effects, ...args) => effects.getConfigured(...args), - setConfigured: (effects, ...args) => effects.setConfigured(...args), - restart: (effects, ...args) => effects.restart(...args), - setDependencies: (effects, ...args) => effects.setDependencies(...args), - checkDependencies: (effects, ...args) => - effects.checkDependencies(...args), - mount: (effects, ...args) => effects.mount(...args), - getInstalledPackages: (effects, ...args) => - effects.getInstalledPackages(...args), - exposeForDependents: (effects, ...args) => - effects.exposeForDependents(...args), - getServicePortForward: (effects, ...args) => - effects.getServicePortForward(...args), - clearBindings: (effects, ...args) => effects.clearBindings(...args), - getContainerIp: (effects, ...args) => effects.getContainerIp(...args), - getSslKey: (effects, ...args) => effects.getSslKey(...args), - setDataVersion: (effects, ...args) => effects.setDataVersion(...args), - getDataVersion: (effects, ...args) => effects.getDataVersion(...args), - shutdown: (effects, ...args) => effects.shutdown(...args), - getDependencies: (effects, ...args) => effects.getDependencies(...args), - } - - return { - ...startSdkEffectWrapper, - - checkDependencies: checkDependencies as < - DependencyId extends keyof Manifest["dependencies"] & - PackageId = keyof Manifest["dependencies"] & PackageId, - >( - effects: Effects, - packageIds?: DependencyId[], - ) => Promise>, - serviceInterface: { - getOwn: (effects: E, id: ServiceInterfaceId) => - removeCallbackTypes(effects)( - getServiceInterface(effects, { - id, - }), - ), - get: ( - effects: E, - opts: { id: ServiceInterfaceId; packageId: PackageId }, - ) => - removeCallbackTypes(effects)(getServiceInterface(effects, opts)), - getAllOwn: (effects: E) => - removeCallbackTypes(effects)(getServiceInterfaces(effects, {})), - getAll: ( - effects: E, - opts: { packageId: PackageId }, - ) => - removeCallbackTypes(effects)(getServiceInterfaces(effects, opts)), - }, - - store: { - get: ( - effects: E, - packageId: string, - path: PathBuilder, - ) => - removeCallbackTypes(effects)( - getStore(effects, path, { - packageId, - }), - ), - getOwn: ( - effects: E, - path: PathBuilder, - ) => - removeCallbackTypes(effects)( - getStore(effects, path), - ), - setOwn: >( - effects: E, - path: Path, - value: Path extends PathBuilder ? Value : never, - ) => - effects.store.set({ - value, - path: extractJsonPath(path), - }), - }, - - host: { - // static: (effects: Effects, id: string) => - // new StaticHost({ id, effects }), - // single: (effects: Effects, id: string) => - // new SingleHost({ id, effects }), - multi: (effects: Effects, id: string) => new MultiHost({ id, effects }), - }, - nullIfEmpty, - runCommand: async ( - effects: Effects, - image: { - id: keyof Manifest["images"] & T.ImageId - sharedRun?: boolean - }, - command: T.CommandType, - options: CommandOptions & { - mounts?: { path: string; options: MountOptions }[] - }, - ): Promise<{ stdout: string | Buffer; stderr: string | Buffer }> => { - return runCommand(effects, image, command, options) - }, - - createAction: < - ConfigType extends - | Record - | Config - | Config, - Type extends Record = ExtractConfigType, - >( - id: string, - metaData: Omit & { - input: Config | Config - }, - fn: (options: { - effects: Effects - input: Type - }) => Promise, - ) => { - const { input, ...rest } = metaData - return createAction( - id, - rest, - fn, - input, - ) - }, - configConstants: { smtpConfig }, - createInterface: ( - effects: Effects, - options: { - name: string - id: string - description: string - hasPrimary: boolean - type: ServiceInterfaceType - username: null | string - path: string - search: Record - schemeOverride: { ssl: Scheme; noSsl: Scheme } | null - masked: boolean - }, - ) => new ServiceInterfaceBuilder({ ...options, effects }), - getSystemSmtp: (effects: E) => - removeCallbackTypes(effects)(new GetSystemSmtp(effects)), - - getSslCerificate: ( - effects: E, - hostnames: string[], - algorithm?: T.Algorithm, - ) => - removeCallbackTypes(effects)( - new GetSslCertificate(effects, hostnames, algorithm), - ), - - createDynamicAction: < - ConfigType extends - | Record - | Config - | Config, - Type extends Record = ExtractConfigType, - >( - id: string, - metaData: (options: { - effects: Effects - }) => MaybePromise>, - fn: (options: { - effects: Effects - input: Type - }) => Promise, - input: Config | Config, - ) => { - return createAction( - id, - metaData, - fn, - input, - ) - }, - HealthCheck: { - of(o: HealthCheckParams) { - return healthCheck(o) - }, - }, - Dependency: { - of(data: Dependency["data"]) { - return new Dependency({ ...data }) - }, - }, - healthCheck: { - checkPortListening, - checkWebUrl, - runHealthScript, - }, - patterns, - setupActions: (...createdActions: CreatedAction[]) => - setupActions(...createdActions), - setupBackups: (...args: SetupBackupsParams) => - setupBackups(this.manifest, ...args), - setupConfig: < - ConfigType extends Config | Config, - Type extends Record = ExtractConfigType, - >( - spec: ConfigType, - write: Save, - read: Read, - ) => setupConfig(spec, write, read), - setupConfigRead: < - ConfigSpec extends - | Config, any> - | Config, never>, - >( - _configSpec: ConfigSpec, - fn: Read, - ) => fn, - setupConfigSave: < - ConfigSpec extends - | Config, any> - | Config, never>, - >( - _configSpec: ConfigSpec, - fn: Save, - ) => fn, - setupDependencyConfig: >( - config: Config | Config, - autoConfigs: { - [K in keyof Manifest["dependencies"]]: DependencyConfig< - Manifest, - Store, - Input, - any - > | null - }, - ) => setupDependencyConfig(config, autoConfigs), - setupDependencies: >( - fn: (options: { - effects: Effects - input: Input | null - }) => Promise, - ) => { - return async (options: { effects: Effects; input: Input }) => { - const dependencyType = await fn(options) - return await options.effects.setDependencies({ - dependencies: Object.entries(dependencyType).map( - ([ - id, - { - data: { versionRange, ...x }, - }, - ]) => ({ - id, - ...x, - ...(x.type === "running" - ? { - kind: "running", - healthChecks: x.healthChecks, - } - : { - kind: "exists", - }), - versionRange: versionRange.toString(), - }), - ), - }) - } - }, - setupInit: ( - versions: VersionGraph, - install: Install, - uninstall: Uninstall, - setInterfaces: SetInterfaces, - setDependencies: (options: { - effects: Effects - input: any - }) => Promise, - exposedStore: ExposedStorePaths, - ) => - setupInit( - versions, - install, - uninstall, - setInterfaces, - setDependencies, - exposedStore, - ), - setupInstall: (fn: InstallFn) => Install.of(fn), - setupInterfaces: < - ConfigInput extends Record, - Output extends InterfacesReceipt, - >( - config: Config, - fn: SetInterfaces, - ) => setupInterfaces(config, fn), - setupMain: ( - fn: (o: { - effects: MainEffects - started(onTerm: () => PromiseLike): PromiseLike - }) => Promise>, - ) => setupMain(fn), - setupProperties: - ( - fn: (options: { effects: Effects }) => Promise, - ): T.ExpectedExports.properties => - (options) => - fn(options).then(nullifyProperties), - setupUninstall: (fn: UninstallFn) => - setupUninstall(fn), - trigger: { - defaultTrigger, - cooldownTrigger, - changeOnFirstSuccess, - successFailure, - }, - Mounts: { - of() { - return Mounts.of() - }, - }, - Backups: { - volumes: ( - ...volumeNames: Array - ) => Backups.volumes(...volumeNames), - addSets: ( - ...options: BackupSet[] - ) => Backups.addSets(...options), - withOptions: (options?: Partial) => - Backups.with_options(options), - }, - Config: { - of: < - Spec extends Record | Value>, - >( - spec: Spec, - ) => Config.of(spec), - }, - Daemons: { - of(config: { - effects: Effects - started: (onTerm: () => PromiseLike) => PromiseLike - healthReceipts: HealthReceipt[] - }) { - return Daemons.of(config) - }, - }, - DependencyConfig: { - of< - LocalConfig extends Record, - RemoteConfig extends Record, - >({ - localConfigSpec, - remoteConfigSpec, - dependencyConfig, - update, - }: { - localConfigSpec: - | Config - | Config - remoteConfigSpec: - | Config - | Config - dependencyConfig: (options: { - effects: Effects - localConfig: LocalConfig - }) => Promise> - update?: Update, RemoteConfig> - }) { - return new DependencyConfig< - Manifest, - Store, - LocalConfig, - RemoteConfig - >(dependencyConfig, update) - }, - }, - List: { - text: List.text, - obj: >( - a: { - name: string - description?: string | null - warning?: string | null - /** Default [] */ - default?: [] - minLength?: number | null - maxLength?: number | null - }, - aSpec: { - spec: Config - displayAs?: null | string - uniqueBy?: null | UniqueBy - }, - ) => List.obj(a, aSpec), - dynamicText: ( - getA: LazyBuild< - Store, - { - name: string - description?: string | null - warning?: string | null - /** Default = [] */ - default?: string[] - minLength?: number | null - maxLength?: number | null - disabled?: false | string - generate?: null | RandomString - spec: { - /** Default = false */ - masked?: boolean - placeholder?: string | null - minLength?: number | null - maxLength?: number | null - patterns: Pattern[] - /** Default = "text" */ - inputmode?: ListValueSpecText["inputmode"] - } - } - >, - ) => List.dynamicText(getA), - }, - StorePath: pathBuilder(), - Value: { - toggle: Value.toggle, - text: Value.text, - textarea: Value.textarea, - number: Value.number, - color: Value.color, - datetime: Value.datetime, - select: Value.select, - multiselect: Value.multiselect, - object: Value.object, - union: Value.union, - list: Value.list, - dynamicToggle: ( - a: LazyBuild< - Store, - { - name: string - description?: string | null - warning?: string | null - default: boolean - disabled?: false | string - } - >, - ) => Value.dynamicToggle(a), - dynamicText: ( - getA: LazyBuild< - Store, - { - name: string - description?: string | null - warning?: string | null - required: RequiredDefault - - /** Default = false */ - masked?: boolean - placeholder?: string | null - minLength?: number | null - maxLength?: number | null - patterns?: Pattern[] - /** Default = 'text' */ - inputmode?: ValueSpecText["inputmode"] - generate?: null | RandomString - } - >, - ) => Value.dynamicText(getA), - dynamicTextarea: ( - getA: LazyBuild< - Store, - { - name: string - description?: string | null - warning?: string | null - required: boolean - minLength?: number | null - maxLength?: number | null - placeholder?: string | null - disabled?: false | string - generate?: null | RandomString - } - >, - ) => Value.dynamicTextarea(getA), - dynamicNumber: ( - getA: LazyBuild< - Store, - { - name: string - description?: string | null - warning?: string | null - required: RequiredDefault - min?: number | null - max?: number | null - /** Default = '1' */ - step?: number | null - integer: boolean - units?: string | null - placeholder?: string | null - disabled?: false | string - } - >, - ) => Value.dynamicNumber(getA), - dynamicColor: ( - getA: LazyBuild< - Store, - { - name: string - description?: string | null - warning?: string | null - required: RequiredDefault - - disabled?: false | string - } - >, - ) => Value.dynamicColor(getA), - dynamicDatetime: ( - getA: LazyBuild< - Store, - { - name: string - description?: string | null - warning?: string | null - required: RequiredDefault - /** Default = 'datetime-local' */ - inputmode?: ValueSpecDatetime["inputmode"] - min?: string | null - max?: string | null - disabled?: false | string - } - >, - ) => Value.dynamicDatetime(getA), - dynamicSelect: ( - getA: LazyBuild< - Store, - { - name: string - description?: string | null - warning?: string | null - required: RequiredDefault - values: Record - disabled?: false | string - } - >, - ) => Value.dynamicSelect(getA), - dynamicMultiselect: ( - getA: LazyBuild< - Store, - { - name: string - description?: string | null - warning?: string | null - default: string[] - values: Record - minLength?: number | null - maxLength?: number | null - disabled?: false | string - } - >, - ) => Value.dynamicMultiselect(getA), - filteredUnion: < - Required extends RequiredDefault, - Type extends Record, - >( - getDisabledFn: LazyBuild, - a: { - name: string - description?: string | null - warning?: string | null - required: Required - }, - aVariants: Variants | Variants, - ) => - Value.filteredUnion( - getDisabledFn, - a, - aVariants, - ), - - dynamicUnion: < - Required extends RequiredDefault, - Type extends Record, - >( - getA: LazyBuild< - Store, - { - disabled: string[] | false | string - name: string - description?: string | null - warning?: string | null - required: Required - } - >, - aVariants: Variants | Variants, - ) => Value.dynamicUnion(getA, aVariants), - }, - Variants: { - of: < - VariantValues extends { - [K in string]: { - name: string - spec: Config - } - }, - >( - a: VariantValues, - ) => Variants.of(a), - }, - } - } -} - -export async function runCommand( - effects: Effects, - image: { id: keyof Manifest["images"] & T.ImageId; sharedRun?: boolean }, - command: string | [string, ...string[]], - options: CommandOptions & { - mounts?: { path: string; options: MountOptions }[] - }, -): Promise<{ stdout: string | Buffer; stderr: string | Buffer }> { - const commands = splitCommand(command) - return SubContainer.with( - effects, - image, - options.mounts || [], - commands.join(" "), - (subcontainer) => subcontainer.exec(commands), - ) -} -function nullifyProperties(value: T.SdkPropertiesReturn): T.PropertiesReturn { - return Object.fromEntries( - Object.entries(value).map(([k, v]) => [k, nullifyProperties_(v)]), - ) -} -function nullifyProperties_(value: T.SdkPropertiesValue): T.PropertiesValue { - if (value.type === "string") { - return { description: null, copyable: null, qr: null, ...value } - } - return { - description: null, - ...value, - value: Object.fromEntries( - Object.entries(value.value).map(([k, v]) => [k, nullifyProperties_(v)]), - ), - } -} diff --git a/sdk/lib/actions/createAction.ts b/sdk/lib/actions/createAction.ts deleted file mode 100644 index 4fa858d56..000000000 --- a/sdk/lib/actions/createAction.ts +++ /dev/null @@ -1,89 +0,0 @@ -import * as T from "../types" -import { Config, ExtractConfigType } from "../config/builder/config" - -import { ActionMetadata, ActionResult, Effects, ExportedAction } from "../types" - -export type MaybeFn = - | Value - | ((options: { effects: Effects }) => Promise | Value) -export class CreatedAction< - Manifest extends T.Manifest, - Store, - ConfigType extends - | Record - | Config - | Config, - Type extends Record = ExtractConfigType, -> { - private constructor( - public readonly id: string, - public readonly myMetadata: MaybeFn< - Manifest, - Store, - Omit - >, - readonly fn: (options: { - effects: Effects - input: Type - }) => Promise, - readonly input: Config, - public validator = input.validator, - ) {} - - static of< - Manifest extends T.Manifest, - Store, - ConfigType extends - | Record - | Config - | Config, - Type extends Record = ExtractConfigType, - >( - id: string, - metadata: MaybeFn>, - fn: (options: { effects: Effects; input: Type }) => Promise, - inputConfig: Config | Config, - ) { - return new CreatedAction( - id, - metadata, - fn, - inputConfig as Config, - ) - } - - exportedAction: ExportedAction = ({ effects, input }) => { - return this.fn({ - effects, - input: this.validator.unsafeCast(input), - }) - } - - run = async ({ effects, input }: { effects: Effects; input?: Type }) => { - return this.fn({ - effects, - input: this.validator.unsafeCast(input), - }) - } - - async metadata(options: { effects: Effects }) { - if (this.myMetadata instanceof Function) - return await this.myMetadata(options) - return this.myMetadata - } - - async ActionMetadata(options: { effects: Effects }): Promise { - return { - ...(await this.metadata(options)), - input: await this.input.build(options), - } - } - - async getConfig({ effects }: { effects: Effects }) { - return this.input.build({ - effects, - }) - } -} - -export const createAction = CreatedAction.of diff --git a/sdk/lib/actions/index.ts b/sdk/lib/actions/index.ts deleted file mode 100644 index 603684b67..000000000 --- a/sdk/lib/actions/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import "./createAction" - -import "./setupActions" diff --git a/sdk/lib/actions/setupActions.ts b/sdk/lib/actions/setupActions.ts deleted file mode 100644 index 07b4e2606..000000000 --- a/sdk/lib/actions/setupActions.ts +++ /dev/null @@ -1,29 +0,0 @@ -import * as T from "../types" -import { Effects, ExpectedExports } from "../types" -import { CreatedAction } from "./createAction" - -export function setupActions( - ...createdActions: CreatedAction[] -) { - const myActions = async (options: { effects: Effects }) => { - const actions: Record> = {} - for (const action of createdActions) { - actions[action.id] = action - } - return actions - } - const answer: { - actions: ExpectedExports.actions - actionsMetadata: ExpectedExports.actionsMetadata - } = { - actions(options: { effects: Effects }) { - return myActions(options) - }, - async actionsMetadata({ effects }: { effects: Effects }) { - return Promise.all( - createdActions.map((x) => x.ActionMetadata({ effects })), - ) - }, - } - return answer -} diff --git a/sdk/lib/backup/Backups.ts b/sdk/lib/backup/Backups.ts deleted file mode 100644 index 031ac4e4c..000000000 --- a/sdk/lib/backup/Backups.ts +++ /dev/null @@ -1,209 +0,0 @@ -import * as T from "../types" - -import * as child_process from "child_process" -import { promises as fsPromises } from "fs" -import { asError } from "../util" - -export type BACKUP = "BACKUP" -export const DEFAULT_OPTIONS: T.BackupOptions = { - delete: true, - force: true, - ignoreExisting: false, - exclude: [], -} -export type BackupSet = { - srcPath: string - srcVolume: Volumes | BACKUP - dstPath: string - dstVolume: Volumes | BACKUP - options?: Partial -} -/** - * This utility simplifies the volume backup process. - * ```ts - * export const { createBackup, restoreBackup } = Backups.volumes("main").build(); - * ``` - * - * Changing the options of the rsync, (ie exludes) use either - * ```ts - * Backups.volumes("main").set_options({exclude: ['bigdata/']}).volumes('excludedVolume').build() - * // or - * Backups.with_options({exclude: ['bigdata/']}).volumes('excludedVolume').build() - * ``` - * - * Using the more fine control, using the addSets for more control - * ```ts - * Backups.addSets({ - * srcVolume: 'main', srcPath:'smallData/', dstPath: 'main/smallData/', dstVolume: : Backups.BACKUP - * }, { - * srcVolume: 'main', srcPath:'bigData/', dstPath: 'main/bigData/', dstVolume: : Backups.BACKUP, options: {exclude:['bigData/excludeThis']}} - * ).build()q - * ``` - */ -export class Backups { - static BACKUP: BACKUP = "BACKUP" - - private constructor( - private options = DEFAULT_OPTIONS, - private backupSet = [] as BackupSet[], - ) {} - static volumes( - ...volumeNames: Array - ): Backups { - return new Backups().addSets( - ...volumeNames.map((srcVolume) => ({ - srcVolume, - srcPath: "./", - dstPath: `./${srcVolume}/`, - dstVolume: Backups.BACKUP, - })), - ) - } - static addSets( - ...options: BackupSet[] - ) { - return new Backups().addSets(...options) - } - static with_options( - options?: Partial, - ) { - return new Backups({ ...DEFAULT_OPTIONS, ...options }) - } - - static withOptions = Backups.with_options - setOptions(options?: Partial) { - this.options = { - ...this.options, - ...options, - } - return this - } - volumes(...volumeNames: Array) { - return this.addSets( - ...volumeNames.map((srcVolume) => ({ - srcVolume, - srcPath: "./", - dstPath: `./${srcVolume}/`, - dstVolume: Backups.BACKUP, - })), - ) - } - addSets(...options: BackupSet[]) { - options.forEach((x) => - this.backupSet.push({ ...x, options: { ...this.options, ...x.options } }), - ) - return this - } - build(pathMaker: T.PathMaker) { - const createBackup: T.ExpectedExports.createBackup = async ({ - effects, - }) => { - for (const item of this.backupSet) { - const rsyncResults = await runRsync( - { - dstPath: item.dstPath, - dstVolume: item.dstVolume, - options: { ...this.options, ...item.options }, - srcPath: item.srcPath, - srcVolume: item.srcVolume, - }, - pathMaker, - ) - await rsyncResults.wait() - } - return - } - const restoreBackup: T.ExpectedExports.restoreBackup = async ({ - effects, - }) => { - for (const item of this.backupSet) { - const rsyncResults = await runRsync( - { - dstPath: item.dstPath, - dstVolume: item.dstVolume, - options: { ...this.options, ...item.options }, - srcPath: item.srcPath, - srcVolume: item.srcVolume, - }, - pathMaker, - ) - await rsyncResults.wait() - } - return - } - return { createBackup, restoreBackup } - } -} -function notEmptyPath(file: string) { - return ["", ".", "./"].indexOf(file) === -1 -} -async function runRsync( - rsyncOptions: { - srcVolume: string - dstVolume: string - srcPath: string - dstPath: string - options: T.BackupOptions - }, - pathMaker: T.PathMaker, -): Promise<{ - id: () => Promise - wait: () => Promise - progress: () => Promise -}> { - const { srcVolume, dstVolume, srcPath, dstPath, options } = rsyncOptions - - const command = "rsync" - const args: string[] = [] - if (options.delete) { - args.push("--delete") - } - if (options.force) { - args.push("--force") - } - if (options.ignoreExisting) { - args.push("--ignore-existing") - } - for (const exclude of options.exclude) { - args.push(`--exclude=${exclude}`) - } - args.push("-actAXH") - args.push("--info=progress2") - args.push("--no-inc-recursive") - args.push(pathMaker({ volume: srcVolume, path: srcPath })) - args.push(pathMaker({ volume: dstVolume, path: dstPath })) - const spawned = child_process.spawn(command, args, { detached: true }) - let percentage = 0.0 - spawned.stdout.on("data", (data: unknown) => { - const lines = String(data).replace("\r", "\n").split("\n") - for (const line of lines) { - const parsed = /$([0-9.]+)%/.exec(line)?.[1] - if (!parsed) continue - percentage = Number.parseFloat(parsed) - } - }) - - spawned.stderr.on("data", (data: unknown) => { - console.error(`Backups.runAsync`, asError(data)) - }) - - const id = async () => { - const pid = spawned.pid - if (pid === undefined) { - throw new Error("rsync process has no pid") - } - return String(pid) - } - const waitPromise = new Promise((resolve, reject) => { - spawned.on("exit", (code: any) => { - if (code === 0) { - resolve(null) - } else { - reject(new Error(`rsync exited with code ${code}`)) - } - }) - }) - const wait = () => waitPromise - const progress = () => Promise.resolve(percentage) - return { id, wait, progress } -} diff --git a/sdk/lib/backup/setupBackups.ts b/sdk/lib/backup/setupBackups.ts deleted file mode 100644 index c12f1d2ed..000000000 --- a/sdk/lib/backup/setupBackups.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Backups } from "./Backups" - -import * as T from "../types" -import { _ } from "../util" - -export type SetupBackupsParams = Array< - M["volumes"][number] | Backups -> - -export function setupBackups( - manifest: M, - ...args: _> -) { - const backups = Array>() - const volumes = new Set() - for (const arg of args) { - if (arg instanceof Backups) { - backups.push(arg) - } else { - volumes.add(arg) - } - } - backups.push(Backups.volumes(...volumes)) - const answer: { - createBackup: T.ExpectedExports.createBackup - restoreBackup: T.ExpectedExports.restoreBackup - } = { - get createBackup() { - return (async (options) => { - for (const backup of backups) { - await backup.build(options.pathMaker).createBackup(options) - } - }) as T.ExpectedExports.createBackup - }, - get restoreBackup() { - return (async (options) => { - for (const backup of backups) { - await backup.build(options.pathMaker).restoreBackup(options) - } - await options.effects.setDataVersion({ version: manifest.version }) - }) as T.ExpectedExports.restoreBackup - }, - } - return answer -} diff --git a/sdk/lib/config/configDependencies.ts b/sdk/lib/config/configDependencies.ts deleted file mode 100644 index d9865f25c..000000000 --- a/sdk/lib/config/configDependencies.ts +++ /dev/null @@ -1,28 +0,0 @@ -import * as T from "../types" - -export type ConfigDependencies = { - exists(id: keyof T["dependencies"]): T.Dependencies[number] - running( - id: keyof T["dependencies"], - healthChecks: string[], - ): T.Dependencies[number] -} - -export const configDependenciesSet = < - T extends T.Manifest, ->(): ConfigDependencies => ({ - exists(id: keyof T["dependencies"]) { - return { - id, - kind: "exists", - } as T.Dependencies[number] - }, - - running(id: keyof T["dependencies"], healthChecks: string[]) { - return { - id, - kind: "running", - healthChecks, - } as T.Dependencies[number] - }, -}) diff --git a/sdk/lib/config/index.ts b/sdk/lib/config/index.ts deleted file mode 100644 index 35c3e274e..000000000 --- a/sdk/lib/config/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * as constants from "./configConstants" -export * as types from "./configTypes" -export * as builder from "./builder" diff --git a/sdk/lib/config/setupConfig.ts b/sdk/lib/config/setupConfig.ts deleted file mode 100644 index f354c81ed..000000000 --- a/sdk/lib/config/setupConfig.ts +++ /dev/null @@ -1,87 +0,0 @@ -import * as T from "../types" - -import * as D from "./configDependencies" -import { Config, ExtractConfigType } from "./builder/config" -import nullIfEmpty from "../util/nullIfEmpty" -import { InterfacesReceipt as InterfacesReceipt } from "../interfaces/setupInterfaces" - -declare const dependencyProof: unique symbol -export type DependenciesReceipt = void & { - [dependencyProof]: never -} - -export type Save< - A extends - | Record - | Config, any> - | Config, never>, -> = (options: { - effects: T.Effects - input: ExtractConfigType & Record -}) => Promise<{ - dependenciesReceipt: DependenciesReceipt - interfacesReceipt: InterfacesReceipt - restart: boolean -}> -export type Read< - Manifest extends T.Manifest, - Store, - A extends - | Record - | Config, any> - | Config, never>, -> = (options: { - effects: T.Effects -}) => Promise & Record)> -/** - * We want to setup a config export with a get and set, this - * is going to be the default helper to setup config, because it will help - * enforce that we have a spec, write, and reading. - * @param options - * @returns - */ -export function setupConfig< - Store, - ConfigType extends - | Record - | Config - | Config, - Manifest extends T.Manifest, - Type extends Record = ExtractConfigType, ->( - spec: Config | Config, - write: Save, - read: Read, -) { - const validator = spec.validator - return { - setConfig: (async ({ effects, input }) => { - if (!validator.test(input)) { - await console.error( - new Error(validator.errorMessage(input)?.toString()), - ) - return { error: "Set config type error for config" } - } - await effects.clearBindings() - await effects.clearServiceInterfaces() - const { restart } = await write({ - input: JSON.parse(JSON.stringify(input)) as any, - effects, - }) - if (restart) { - await effects.restart() - } - }) as T.ExpectedExports.setConfig, - getConfig: (async ({ effects }) => { - const configValue = nullIfEmpty((await read({ effects })) || null) - return { - spec: await spec.build({ - effects, - }), - config: configValue, - } - }) as T.ExpectedExports.getConfig, - } -} - -export default setupConfig diff --git a/sdk/lib/dependencies/DependencyConfig.ts b/sdk/lib/dependencies/DependencyConfig.ts deleted file mode 100644 index b48bf56d3..000000000 --- a/sdk/lib/dependencies/DependencyConfig.ts +++ /dev/null @@ -1,39 +0,0 @@ -import * as T from "../types" -import { deepEqual } from "../util/deepEqual" -import { deepMerge } from "../util/deepMerge" - -export type Update = (options: { - remoteConfig: RemoteConfig - queryResults: QueryResults -}) => Promise - -export class DependencyConfig< - Manifest extends T.Manifest, - Store, - Input extends Record, - RemoteConfig extends Record, -> { - static defaultUpdate = async (options: { - queryResults: unknown - remoteConfig: unknown - }): Promise => { - return deepMerge({}, options.remoteConfig, options.queryResults || {}) - } - constructor( - readonly dependencyConfig: (options: { - effects: T.Effects - localConfig: Input - }) => Promise>, - readonly update: Update< - void | T.DeepPartial, - RemoteConfig - > = DependencyConfig.defaultUpdate as any, - ) {} - - async query(options: { effects: T.Effects; localConfig: unknown }) { - return this.dependencyConfig({ - localConfig: options.localConfig as Input, - effects: options.effects, - }) - } -} diff --git a/sdk/lib/dependencies/setupDependencyConfig.ts b/sdk/lib/dependencies/setupDependencyConfig.ts deleted file mode 100644 index 2fde4bce5..000000000 --- a/sdk/lib/dependencies/setupDependencyConfig.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Config } from "../config/builder/config" - -import * as T from "../types" -import { DependencyConfig } from "./DependencyConfig" - -export function setupDependencyConfig< - Store, - Input extends Record, - Manifest extends T.Manifest, ->( - _config: Config | Config, - autoConfigs: { - [key in keyof Manifest["dependencies"] & string]: DependencyConfig< - Manifest, - Store, - Input, - any - > | null - }, -): T.ExpectedExports.dependencyConfig { - return autoConfigs -} diff --git a/sdk/lib/health/HealthReceipt.ts b/sdk/lib/health/HealthReceipt.ts deleted file mode 100644 index a0995ba0a..000000000 --- a/sdk/lib/health/HealthReceipt.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare const HealthProof: unique symbol -export type HealthReceipt = { - [HealthProof]: never -} diff --git a/sdk/lib/health/index.ts b/sdk/lib/health/index.ts deleted file mode 100644 index b6e1d26f5..000000000 --- a/sdk/lib/health/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import "./checkFns" - -import "./HealthReceipt" diff --git a/sdk/lib/interfaces/setupInterfaces.ts b/sdk/lib/interfaces/setupInterfaces.ts deleted file mode 100644 index c82b69e0b..000000000 --- a/sdk/lib/interfaces/setupInterfaces.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Config } from "../config/builder/config" - -import * as T from "../types" -import { AddressReceipt } from "./AddressReceipt" - -export type InterfacesReceipt = Array -export type SetInterfaces< - Manifest extends T.Manifest, - Store, - ConfigInput extends Record, - Output extends InterfacesReceipt, -> = (opts: { effects: T.Effects; input: null | ConfigInput }) => Promise -export type SetupInterfaces = < - Manifest extends T.Manifest, - Store, - ConfigInput extends Record, - Output extends InterfacesReceipt, ->( - config: Config, - fn: SetInterfaces, -) => SetInterfaces -export const NO_INTERFACE_CHANGES = [] as InterfacesReceipt -export const setupInterfaces: SetupInterfaces = (_config, fn) => fn diff --git a/sdk/lib/manifest/ManifestTypes.ts b/sdk/lib/manifest/ManifestTypes.ts deleted file mode 100644 index cc564de2d..000000000 --- a/sdk/lib/manifest/ManifestTypes.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { ValidateExVer, ValidateExVers } from "../exver" -import { - ActionMetadata, - HardwareRequirements, - ImageConfig, - ImageId, - ImageSource, -} from "../types" - -export type SDKManifest = { - /** The package identifier used by the OS. This must be unique amongst all other known packages */ - readonly id: string - /** A human readable service title */ - readonly title: string - /** The type of license for the project. Include the LICENSE in the root of the project directory. A license is required for a Start9 package.*/ - readonly license: string // name of license - /** The Start9 wrapper repository URL for the package. This repo contains the manifest file (this), - * any scripts necessary for configuration, backups, actions, or health checks (more below). This key - * must exist. But could be embedded into the source repository - */ - readonly wrapperRepo: string - /** The original project repository URL. There is no upstream repo in this example */ - readonly upstreamRepo: string - /** URL to the support site / channel for the project. This key can be omitted if none exists, or it can link to the original project repository issues */ - readonly supportSite: string - /** URL to the marketing site for the project. If there is no marketing site, it can link to the original project repository */ - readonly marketingSite: string - /** URL where users can donate to the upstream project */ - readonly donationUrl: string | null - /**Human readable descriptions for the service. These are used throughout the StartOS user interface, primarily in the marketplace. */ - readonly description: { - /**This is the first description visible to the user in the marketplace */ - readonly short: string - /** This description will display with additional details in the service's individual marketplace page */ - readonly long: string - } - - /** Defines the os images needed to run the container processes */ - readonly images: Record - /** This denotes readonly asset directories that should be available to mount to the container. - * These directories are expected to be found in `assets/` at pack time. - **/ - readonly assets: string[] - /** This denotes any data volumes that should be available to mount to the container */ - readonly volumes: string[] - - readonly alerts?: { - readonly install?: string | null - readonly update?: string | null - readonly uninstall?: string | null - readonly restore?: string | null - readonly start?: string | null - readonly stop?: string | null - } - readonly hasConfig?: boolean - readonly dependencies: Readonly> - readonly hardwareRequirements?: { - readonly device?: { display?: RegExp; processor?: RegExp } - readonly ram?: number | null - readonly arch?: string[] | null - } -} - -export type SDKImageConfig = { - source: Exclude - arch?: string[] - emulateMissingAs?: string | null -} - -export type ManifestDependency = { - /** - * A human readable explanation on what the dependency is used for - */ - readonly description: string | null - /** - * Determines if the dependency is optional or not. Times that optional that are good include such situations - * such as being able to toggle other services or to use a different service for the same purpose. - */ - readonly optional: boolean - /** - * A url or local path for an s9pk that satisfies this dependency - */ - readonly s9pk: string -} diff --git a/sdk/lib/manifest/index.ts b/sdk/lib/manifest/index.ts deleted file mode 100644 index 806ef5e61..000000000 --- a/sdk/lib/manifest/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -import "./setupManifest" -import "./ManifestTypes" diff --git a/sdk/lib/store/setupExposeStore.ts b/sdk/lib/store/setupExposeStore.ts deleted file mode 100644 index 9272a9a6b..000000000 --- a/sdk/lib/store/setupExposeStore.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Affine, _ } from "../util" -import { PathBuilder, extractJsonPath, pathBuilder } from "./PathBuilder" - -export type ExposedStorePaths = string[] & Affine<"ExposedStorePaths"> - -export const setupExposeStore = >( - fn: (pathBuilder: PathBuilder) => PathBuilder[], -) => { - return fn(pathBuilder()).map( - (x) => extractJsonPath(x) as string, - ) as ExposedStorePaths -} diff --git a/sdk/lib/test/setupDependencyConfig.test.ts b/sdk/lib/test/setupDependencyConfig.test.ts deleted file mode 100644 index 622559eb6..000000000 --- a/sdk/lib/test/setupDependencyConfig.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { sdk } from "./output.sdk" - -describe("setupDependencyConfig", () => { - test("test", () => { - const testConfig = sdk.Config.of({ - test: sdk.Value.text({ - name: "testValue", - required: false, - }), - }) - - const testConfig2 = sdk.Config.of({ - test2: sdk.Value.text({ - name: "testValue2", - required: false, - }), - }) - const remoteTest = sdk.DependencyConfig.of({ - localConfigSpec: testConfig, - remoteConfigSpec: testConfig2, - dependencyConfig: async ({}) => {}, - }) - sdk.setupDependencyConfig(testConfig, { - "remote-test": remoteTest, - }) - }) -}) diff --git a/sdk/lib/types.ts b/sdk/lib/types.ts deleted file mode 100644 index 68bd7b588..000000000 --- a/sdk/lib/types.ts +++ /dev/null @@ -1,523 +0,0 @@ -export * as configTypes from "./config/configTypes" - -import { - DependencyRequirement, - SetHealth, - NamedHealthCheckResult, - SetMainStatus, - ServiceInterface, - Host, - ExportServiceInterfaceParams, - GetPrimaryUrlParams, - LanInfo, - BindParams, - Manifest, - CheckDependenciesResult, - ActionId, - HostId, -} from "./osBindings" - -import { MainEffects, ServiceInterfaceType, Signals } from "./StartSdk" -import { InputSpec } from "./config/configTypes" -import { DependenciesReceipt } from "./config/setupConfig" -import { BindOptions, Scheme } from "./interfaces/Host" -import { Daemons } from "./mainFn/Daemons" -import { StorePath } from "./store/PathBuilder" -import { ExposedStorePaths } from "./store/setupExposeStore" -import { UrlString } from "./util/getServiceInterface" -import { StringObject, ToKebab } from "./util" -export * from "./osBindings" -export { SDKManifest } from "./manifest/ManifestTypes" -export { HealthReceipt } from "./health/HealthReceipt" - -export type PathMaker = (options: { volume: string; path: string }) => string -export type ExportedAction = (options: { - effects: Effects - input?: Record -}) => Promise -export type MaybePromise = Promise | A -export namespace ExpectedExports { - version: 1 - /** Set configuration is called after we have modified and saved the configuration in the start9 ui. Use this to make a file for the docker to read from for configuration. */ - export type setConfig = (options: { - effects: Effects - input: Record - }) => Promise - /** Get configuration returns a shape that describes the format that the start9 ui will generate, and later send to the set config */ - export type getConfig = (options: { effects: Effects }) => Promise - // /** These are how we make sure the our dependency configurations are valid and if not how to fix them. */ - // export type dependencies = Dependencies; - /** For backing up service data though the startOS UI */ - export type createBackup = (options: { - effects: Effects - pathMaker: PathMaker - }) => Promise - /** For restoring service data that was previously backed up using the startOS UI create backup flow. Backup restores are also triggered via the startOS UI, or doing a system restore flow during setup. */ - export type restoreBackup = (options: { - effects: Effects - pathMaker: PathMaker - }) => Promise - - // /** Health checks are used to determine if the service is working properly after starting - // * A good use case is if we are using a web server, seeing if we can get to the web server. - // */ - // export type health = { - // /** Should be the health check id */ - // [id: string]: (options: { effects: Effects; input: TimeMs }) => Promise; - // }; - - /** - * Actions are used so we can effect the service, like deleting a directory. - * One old use case is to add a action where we add a file, that will then be run during the - * service starting, and that file would indicate that it would rescan all the data. - */ - export type actions = (options: { effects: Effects }) => MaybePromise<{ - [id: string]: { - run: ExportedAction - getConfig: (options: { effects: Effects }) => Promise - } - }> - - export type actionsMetadata = (options: { - effects: Effects - }) => Promise> - - /** - * This is the entrypoint for the main container. Used to start up something like the service that the - * package represents, like running a bitcoind in a bitcoind-wrapper. - */ - export type main = (options: { - effects: MainEffects - started(onTerm: () => PromiseLike): PromiseLike - }) => Promise> - - /** - * After a shutdown, if we wanted to do any operations to clean up things, like - * set the action as unavailable or something. - */ - export type afterShutdown = (options: { - effects: Effects - }) => Promise - - /** - * Every time a package completes an install, this function is called before the main. - * Can be used to do migration like things. - */ - export type init = (options: { effects: Effects }) => Promise - /** This will be ran during any time a package is uninstalled, for example during a update - * this will be called. - */ - export type uninit = (options: { - effects: Effects - nextVersion: null | string - }) => Promise - - /** Auto configure is used to make sure that other dependencies have the values t - * that this service could use. - */ - export type dependencyConfig = Record - - export type properties = (options: { - effects: Effects - }) => Promise - - export type manifest = Manifest -} -export type ABI = { - setConfig: ExpectedExports.setConfig - getConfig: ExpectedExports.getConfig - createBackup: ExpectedExports.createBackup - restoreBackup: ExpectedExports.restoreBackup - actions: ExpectedExports.actions - actionsMetadata: ExpectedExports.actionsMetadata - main: ExpectedExports.main - afterShutdown: ExpectedExports.afterShutdown - init: ExpectedExports.init - uninit: ExpectedExports.uninit - dependencyConfig: ExpectedExports.dependencyConfig - properties: ExpectedExports.properties - manifest: ExpectedExports.manifest -} -export type TimeMs = number -export type VersionString = string - -/** - * AutoConfigure is used as the value to the key of package id, - * this is used to make sure that other dependencies have the values that this service could use. - */ -export type DependencyConfig = { - /** During autoconfigure, we have access to effects and local data. We are going to figure out all the data that we need and send it to update. For the sdk it is the desired delta */ - query(options: { effects: Effects }): Promise - /** This is the second part. Given the query results off the previous function, we will determine what to change the remote config to. In our sdk normall we are going to use the previous as a deep merge. */ - update(options: { - queryResults: unknown - remoteConfig: unknown - }): Promise -} - -export type ConfigRes = { - /** This should be the previous config, that way during set config we start with the previous */ - config?: null | Record - /** Shape that is describing the form in the ui */ - spec: InputSpec -} - -declare const DaemonProof: unique symbol -export type DaemonReceipt = { - [DaemonProof]: never -} -export type Daemon = { - wait(): Promise - term(): Promise - [DaemonProof]: never -} - -export type HealthStatus = NamedHealthCheckResult["result"] -export type SmtpValue = { - server: string - port: number - from: string - login: string - password: string | null | undefined -} - -export type CommandType = string | [string, ...string[]] - -export type DaemonReturned = { - wait(): Promise - term(options?: { signal?: Signals; timeout?: number }): Promise -} - -export type ActionMetadata = { - name: string - description: string - warning: string | null - input: InputSpec - disabled: boolean - allowedStatuses: "onlyRunning" | "onlyStopped" | "any" - /** - * So the ordering of the actions is by alphabetical order of the group, then followed by the alphabetical of the actions - */ - group: string | null -} -export declare const hostName: unique symbol -// asdflkjadsf.onion | 1.2.3.4 -export type Hostname = string & { [hostName]: never } - -export type HostnameInfoIp = { - kind: "ip" - networkInterfaceId: string - public: boolean - hostname: - | { - kind: "ipv4" | "ipv6" | "local" - value: string - port: number | null - sslPort: number | null - } - | { - kind: "domain" - domain: string - subdomain: string | null - port: number | null - sslPort: number | null - } -} - -export type HostnameInfoOnion = { - kind: "onion" - hostname: { value: string; port: number | null; sslPort: number | null } -} - -export type HostnameInfo = HostnameInfoIp | HostnameInfoOnion - -export type ServiceInterfaceId = string - -export { ServiceInterface } -export type ExposeServicePaths = { - /** The path to the value in the Store. [JsonPath](https://jsonpath.com/) */ - paths: ExposedStorePaths -} - -export type SdkPropertiesValue = - | { - type: "object" - value: { [k: string]: SdkPropertiesValue } - description?: string - } - | { - type: "string" - /** The value to display to the user */ - value: string - /** A human readable description or explanation of the value */ - description?: string - /** Whether or not to mask the value, for example, when displaying a password */ - masked: boolean - /** Whether or not to include a button for copying the value to clipboard */ - copyable?: boolean - /** Whether or not to include a button for displaying the value as a QR code */ - qr?: boolean - } - -export type SdkPropertiesReturn = { - [key: string]: SdkPropertiesValue -} - -export type PropertiesValue = - | { - type: "object" - value: { [k: string]: PropertiesValue } - description: string | null - } - | { - type: "string" - /** The value to display to the user */ - value: string - /** A human readable description or explanation of the value */ - description: string | null - /** Whether or not to mask the value, for example, when displaying a password */ - masked: boolean - /** Whether or not to include a button for copying the value to clipboard */ - copyable: boolean | null - /** Whether or not to include a button for displaying the value as a QR code */ - qr: boolean | null - } - -export type PropertiesReturn = { - [key: string]: PropertiesValue -} - -export type EffectMethod = { - [K in keyof T]-?: K extends string - ? T[K] extends Function - ? ToKebab - : T[K] extends StringObject - ? `${ToKebab}.${EffectMethod}` - : never - : never -}[keyof T] - -/** Used to reach out from the pure js runtime */ -export type Effects = { - // action - - /** Run an action exported by a service */ - executeAction(opts: { - packageId?: PackageId - actionId: ActionId - input: Input - }): Promise - /** Define an action that can be invoked by a user or service */ - exportAction(options: { - id: ActionId - metadata: ActionMetadata - }): Promise - /** Remove all exported actions */ - clearActions(): Promise - - // config - - /** Returns whether or not the package has been configured */ - getConfigured(options: { packageId?: PackageId }): Promise - /** Indicates that this package has been configured. Called during setConfig or init */ - setConfigured(options: { configured: boolean }): Promise - - // control - - /** restart this service's main function */ - restart(): Promise - /** stop this service's main function */ - shutdown(): Promise - /** indicate to the host os what runstate the service is in */ - setMainStatus(options: SetMainStatus): Promise - - // dependency - - /** Set the dependencies of what the service needs, usually run during the set config as a best practice */ - setDependencies(options: { - dependencies: Dependencies - }): Promise - /** Get the list of the dependencies, both the dynamic set by the effect of setDependencies and the end result any required in the manifest */ - getDependencies(): Promise - /** Test whether current dependency requirements are satisfied */ - checkDependencies(options: { - packageIds?: PackageId[] - }): Promise - /** mount a volume of a dependency */ - mount(options: { - location: string - target: { - packageId: string - volumeId: string - subpath: string | null - readonly: boolean - } - }): Promise - /** Returns a list of the ids of all installed packages */ - getInstalledPackages(): Promise - /** grants access to certain paths in the store to dependents */ - exposeForDependents(options: { paths: string[] }): Promise - - // health - - /** sets the result of a health check */ - setHealth(o: SetHealth): Promise - - // subcontainer - subcontainer: { - /** A low level api used by SubContainer */ - createFs(options: { - imageId: string - name: string | null - }): Promise<[string, string]> - /** A low level api used by SubContainer */ - destroyFs(options: { guid: string }): Promise - } - - // net - - // bind - /** Creates a host connected to the specified port with the provided options */ - bind(options: BindParams): Promise - /** Get the port address for a service */ - getServicePortForward(options: { - packageId?: PackageId - hostId: HostId - internalPort: number - }): Promise - /** Removes all network bindings, called in the setupConfig */ - clearBindings(): Promise - // host - /** Returns information about the specified host, if it exists */ - getHostInfo(options: { - packageId?: PackageId - hostId: HostId - callback?: () => void - }): Promise - /** Returns the primary url that a user has selected for a host, if it exists */ - getPrimaryUrl(options: { - packageId?: PackageId - hostId: HostId - callback?: () => void - }): Promise - /** Returns the IP address of the container */ - getContainerIp(): Promise - // interface - /** Creates an interface bound to a specific host and port to show to the user */ - exportServiceInterface(options: ExportServiceInterfaceParams): Promise - /** Returns an exported service interface */ - getServiceInterface(options: { - packageId?: PackageId - serviceInterfaceId: ServiceInterfaceId - callback?: () => void - }): Promise - /** Returns all exported service interfaces for a package */ - listServiceInterfaces(options: { - packageId?: PackageId - callback?: () => void - }): Promise> - /** Removes all service interfaces */ - clearServiceInterfaces(): Promise - // ssl - /** Returns a PEM encoded fullchain for the hostnames specified */ - getSslCertificate: (options: { - hostnames: string[] - algorithm?: "ecdsa" | "ed25519" - callback?: () => void - }) => Promise<[string, string, string]> - /** Returns a PEM encoded private key corresponding to the certificate for the hostnames specified */ - getSslKey: (options: { - hostnames: string[] - algorithm?: "ecdsa" | "ed25519" - }) => Promise - - // store - - store: { - /** Get a value in a json like data, can be observed and subscribed */ - get(options: { - /** If there is no packageId it is assumed the current package */ - packageId?: string - /** The path defaults to root level, using the [JsonPath](https://jsonpath.com/) */ - path: StorePath - callback?: () => void - }): Promise - /** Used to store values that can be accessed and subscribed to */ - set(options: { - /** Sets the value for the wrapper at the path, it will override, using the [JsonPath](https://jsonpath.com/) */ - path: StorePath - value: ExtractStore - }): Promise - } - /** sets the version that this service's data has been migrated to */ - setDataVersion(options: { version: string }): Promise - /** returns the version that this service's data has been migrated to */ - getDataVersion(): Promise - - // system - - /** Returns globally configured SMTP settings, if they exist */ - getSystemSmtp(options: { callback?: () => void }): Promise -} - -/** rsync options: https://linux.die.net/man/1/rsync - */ -export type BackupOptions = { - delete: boolean - force: boolean - ignoreExisting: boolean - exclude: string[] -} -/** - * This is the metadata that is returned from the metadata call. - */ -export type Metadata = { - fileType: string - isDir: boolean - isFile: boolean - isSymlink: boolean - len: number - modified?: Date - accessed?: Date - created?: Date - readonly: boolean - uid: number - gid: number - mode: number -} - -export type MigrationRes = { - configured: boolean -} - -export type ActionResult = { - version: "0" - message: string - value: string | null - copyable: boolean - qr: boolean -} -export type SetResult = { - dependsOn: DependsOn - signal: Signals -} - -export type PackageId = string -export type Message = string -export type DependencyKind = "running" | "exists" - -export type DependsOn = { - [packageId: string]: string[] | readonly string[] -} - -export type KnownError = - | { error: string } - | { - errorCode: [number, string] | readonly [number, string] - } - -export type Dependencies = Array - -export type DeepPartial = T extends {} - ? { [P in keyof T]?: DeepPartial } - : T diff --git a/sdk/lib/util/asError.ts b/sdk/lib/util/asError.ts deleted file mode 100644 index 6e98afb6a..000000000 --- a/sdk/lib/util/asError.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const asError = (e: unknown) => { - if (e instanceof Error) { - return new Error(e as any) - } - return new Error(`${e}`) -} diff --git a/sdk/lib/util/deepMerge.ts b/sdk/lib/util/deepMerge.ts deleted file mode 100644 index ae68c242f..000000000 --- a/sdk/lib/util/deepMerge.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { object } from "ts-matches" - -export function deepMerge(...args: unknown[]): unknown { - const lastItem = (args as any)[args.length - 1] - if (!object.test(lastItem)) return lastItem - const objects = args.filter(object.test).filter((x) => !Array.isArray(x)) - if (objects.length === 0) return lastItem as any - if (objects.length === 1) objects.unshift({}) - const allKeys = new Set(objects.flatMap((x) => Object.keys(x))) - for (const key of allKeys) { - const filteredValues = objects.flatMap((x) => - key in x ? [(x as any)[key]] : [], - ) - ;(objects as any)[0][key] = deepMerge(...filteredValues) - } - return objects[0] as any -} diff --git a/sdk/lib/util/fileHelper.ts b/sdk/lib/util/fileHelper.ts deleted file mode 100644 index 383b4fd31..000000000 --- a/sdk/lib/util/fileHelper.ts +++ /dev/null @@ -1,150 +0,0 @@ -import * as matches from "ts-matches" -import * as YAML from "yaml" -import * as TOML from "@iarna/toml" -import merge from "lodash.merge" -import * as T from "../types" -import * as fs from "node:fs/promises" - -const previousPath = /(.+?)\/([^/]*)$/ - -/** - * Used in the get config and the set config exported functions. - * The idea is that we are going to be reading/ writing to a file, or multiple files. And then we use this tool - * to keep the same path on the read and write, and have methods for helping with structured data. - * And if we are not using a structured data, we can use the raw method which forces the construction of a BiMap - * ```ts - import {InputSpec} from './InputSpec.ts' - import {matches, T} from '../deps.ts'; - const { object, string, number, boolean, arrayOf, array, anyOf, allOf } = matches - const someValidator = object({ - data: string - }) - const jsonFile = FileHelper.json({ - path: 'data.json', - validator: someValidator, - volume: 'main' - }) - const tomlFile = FileHelper.toml({ - path: 'data.toml', - validator: someValidator, - volume: 'main' - }) - const rawFile = FileHelper.raw({ - path: 'data.amazingSettings', - volume: 'main' - fromData(dataIn: Data): string { - return `myDatais ///- ${dataIn.data}` - }, - toData(rawData: string): Data { - const [,data] = /myDatais \/\/\/- (.*)/.match(rawData) - return {data} - } - }) - - export const setConfig : T.ExpectedExports.setConfig= async (effects, config) => { - await jsonFile.write({ data: 'here lies data'}, effects) - } - - export const getConfig: T.ExpectedExports.getConfig = async (effects, config) => ({ - spec: InputSpec, - config: nullIfEmpty({ - ...jsonFile.get(effects) - }) - ``` - */ -export class FileHelper { - protected constructor( - readonly path: string, - readonly writeData: (dataIn: A) => string, - readonly readData: (stringValue: string) => A, - ) {} - async write(data: A, effects: T.Effects) { - const parent = previousPath.exec(this.path) - if (parent) { - await fs.mkdir(parent[1], { recursive: true }) - } - - await fs.writeFile(this.path, this.writeData(data)) - } - async read(effects: T.Effects) { - if ( - !(await fs.access(this.path).then( - () => true, - () => false, - )) - ) { - return null - } - return this.readData( - await fs.readFile(this.path).then((data) => data.toString("utf-8")), - ) - } - - async merge(data: A, effects: T.Effects) { - const fileData = (await this.read(effects).catch(() => ({}))) || {} - const mergeData = merge({}, fileData, data) - return await this.write(mergeData, effects) - } - /** - * Create a File Helper for an arbitrary file type. - * - * Provide custom functions for translating data to the file format and visa versa. - */ - static raw( - path: string, - toFile: (dataIn: A) => string, - fromFile: (rawData: string) => A, - ) { - return new FileHelper(path, toFile, fromFile) - } - /** - * Create a File Helper for a .json file - */ - static json(path: string, shape: matches.Validator) { - return new FileHelper( - path, - (inData) => { - return JSON.stringify(inData, null, 2) - }, - (inString) => { - return shape.unsafeCast(JSON.parse(inString)) - }, - ) - } - /** - * Create a File Helper for a .toml file - */ - static toml>( - path: string, - shape: matches.Validator, - ) { - return new FileHelper( - path, - (inData) => { - return TOML.stringify(inData as any) - }, - (inString) => { - return shape.unsafeCast(TOML.parse(inString)) - }, - ) - } - /** - * Create a File Helper for a .yaml file - */ - static yaml>( - path: string, - shape: matches.Validator, - ) { - return new FileHelper( - path, - (inData) => { - return YAML.stringify(inData, null, 2) - }, - (inString) => { - return shape.unsafeCast(YAML.parse(inString)) - }, - ) - } -} - -export default FileHelper diff --git a/sdk/lib/util/index.browser.ts b/sdk/lib/util/index.browser.ts deleted file mode 100644 index 94339e7f7..000000000 --- a/sdk/lib/util/index.browser.ts +++ /dev/null @@ -1,10 +0,0 @@ -import * as T from "../types" - -/// Currently being used -export { addressHostToUrl } from "./getServiceInterface" -export { getDefaultString } from "./getDefaultString" - -/// Not being used, but known to be browser compatible -export { GetServiceInterface, getServiceInterface } from "./getServiceInterface" -export { getServiceInterfaces } from "./getServiceInterfaces" -export * from "./typeHelpers" diff --git a/sdk/lib/util/index.ts b/sdk/lib/util/index.ts deleted file mode 100644 index 9246cf791..000000000 --- a/sdk/lib/util/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -import "./nullIfEmpty" -import "./fileHelper" -import "../store/getStore" -import "./deepEqual" -import "./deepMerge" -import "./SubContainer" -import "./once" - -export { GetServiceInterface, getServiceInterface } from "./getServiceInterface" -export { asError } from "./asError" -export { getServiceInterfaces } from "./getServiceInterfaces" -export { addressHostToUrl } from "./getServiceInterface" -export { hostnameInfoToAddress } from "./Hostname" -export * from "./typeHelpers" -export { getDefaultString } from "./getDefaultString" -export { inMs } from "./inMs" diff --git a/sdk/lib/util/nullIfEmpty.ts b/sdk/lib/util/nullIfEmpty.ts deleted file mode 100644 index 337b9098f..000000000 --- a/sdk/lib/util/nullIfEmpty.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * A useful tool when doing a getConfig. - * Look into the config {@link FileHelper} for an example of the use. - * @param s - * @returns - */ -export default function nullIfEmpty>( - s: null | A, -) { - if (s === null) return null - return Object.keys(s).length === 0 ? null : s -} diff --git a/sdk/package-lock.json b/sdk/package-lock.json index 59f7ab718..9699e0851 100644 --- a/sdk/package-lock.json +++ b/sdk/package-lock.json @@ -1,4676 +1,6 @@ { - "name": "@start9labs/start-sdk", - "version": "0.3.6-alpha8", + "name": "sdk", "lockfileVersion": 3, "requires": true, - "packages": { - "": { - "name": "@start9labs/start-sdk", - "version": "0.3.6-alpha8", - "license": "MIT", - "dependencies": { - "@iarna/toml": "^2.2.5", - "@noble/curves": "^1.4.0", - "@noble/hashes": "^1.4.0", - "isomorphic-fetch": "^3.0.0", - "lodash.merge": "^4.6.2", - "mime": "^4.0.3", - "ts-matches": "^5.5.1", - "yaml": "^2.2.2" - }, - "devDependencies": { - "@types/jest": "^29.4.0", - "@types/lodash.merge": "^4.6.2", - "jest": "^29.4.3", - "peggy": "^3.0.2", - "prettier": "^3.2.5", - "ts-jest": "^29.0.5", - "ts-node": "^10.9.1", - "ts-pegjs": "^4.2.1", - "tsx": "^4.7.1", - "typescript": "^5.0.4" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", - "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.3.tgz", - "integrity": "sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.3", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.21.2", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.3", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.3", - "@babel/types": "^7.21.3", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/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/@babel/generator": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.3.tgz", - "integrity": "sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.21.3", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "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/@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "lru-cache": "^5.1.1", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", - "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.2", - "@babel/types": "^7.21.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", - "dev": true, - "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/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/@babel/highlight/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/@babel/highlight/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/@babel/highlight/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/@babel/highlight/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/@babel/highlight/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/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz", - "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "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==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "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==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "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==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "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==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "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==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", - "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "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==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "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==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "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==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "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==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "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==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "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==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "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==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.3.tgz", - "integrity": "sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.3", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.3", - "@babel/types": "^7.21.3", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz", - "integrity": "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "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/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@iarna/toml": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", - "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" - }, - "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==", - "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" - }, - "engines": { - "node": ">=8" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.5.0.tgz", - "integrity": "sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.5.0.tgz", - "integrity": "sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.5.0", - "@jest/reporters": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@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.5.0", - "jest-config": "^29.5.0", - "jest-haste-map": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-resolve-dependencies": "^29.5.0", - "jest-runner": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "jest-watcher": "^29.5.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.5.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "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/environment": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz", - "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "jest-mock": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.5.0.tgz", - "integrity": "sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==", - "dev": true, - "dependencies": { - "expect": "^29.5.0", - "jest-snapshot": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", - "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.4.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz", - "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.5.0", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.5.0", - "jest-mock": "^29.5.0", - "jest-util": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.5.0.tgz", - "integrity": "sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/expect": "^29.5.0", - "@jest/types": "^29.5.0", - "jest-mock": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.5.0.tgz", - "integrity": "sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@jridgewell/trace-mapping": "^0.3.15", - "@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": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", - "jest-worker": "^29.5.0", - "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" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", - "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.25.16" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.3.tgz", - "integrity": "sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.15", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.5.0.tgz", - "integrity": "sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz", - "integrity": "sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.5.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.5.0.tgz", - "integrity": "sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.5.0", - "@jridgewell/trace-mapping": "^0.3.15", - "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.5.0", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.5.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.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/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "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.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "node_modules/@noble/curves": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.0.tgz", - "integrity": "sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==", - "dependencies": { - "@noble/hashes": "1.4.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/hashes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", - "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "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": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.25.24", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", - "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", - "dev": true - }, - "node_modules/@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", - "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^2.0.0" - } - }, - "node_modules/@ts-morph/common": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.19.0.tgz", - "integrity": "sha512-Unz/WHmd4pGax91rdIKWi51wnVUW11QttMEPpBiBgIewnc9UQIX7UDLxr5vRlqeByXCwhkF6VabSsI0raWcyAQ==", - "dev": true, - "dependencies": { - "fast-glob": "^3.2.12", - "minimatch": "^7.4.3", - "mkdirp": "^2.1.6", - "path-browserify": "^1.0.1" - } - }, - "node_modules/@ts-morph/common/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@ts-morph/common/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==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true - }, - "node_modules/@types/babel__core": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", - "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", - "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.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.3.0" - } - }, - "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.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.0.tgz", - "integrity": "sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==", - "dev": true, - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/lodash": { - "version": "4.17.5", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.5.tgz", - "integrity": "sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==", - "dev": true - }, - "node_modules/@types/lodash.merge": { - "version": "4.6.9", - "resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.9.tgz", - "integrity": "sha512-23sHDPmzd59kUgWyKGiOMO2Qb9YtqRO/x4IhkgNUiPQ1+5MUVqi6bCZeq9nBJ17msjIMbEIO5u+XW4Kz6aGUhQ==", - "dev": true, - "dependencies": { - "@types/lodash": "*" - } - }, - "node_modules/@types/node": { - "version": "18.15.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.10.tgz", - "integrity": "sha512-9avDaQJczATcXgfmMAW3MIWArOO7A+m90vuCFLr8AotWf8igO/mRoYukrk2cqZVtv38tHs33retzHEilM7FpeQ==", - "dev": true - }, - "node_modules/@types/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", - "dev": true - }, - "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/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/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "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/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-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/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/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "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/babel-jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz", - "integrity": "sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.5.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.5.0", - "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-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.5.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", - "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", - "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.5.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", - "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^29.5.0", - "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==", - "dev": true - }, - "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/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.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" - }, - "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/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.30001470", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001470.tgz", - "integrity": "sha512-065uNwY6QtHCBOExzbV6m236DDhYCCtPmQUCoQtwkVqzud8v5QPidoMr6CoMkC2nfp6nksjttqWQRRh75LqUmA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - } - ] - }, - "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/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.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", - "dev": true - }, - "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/cliui/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/cliui/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/cliui/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==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "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/code-block-writer": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz", - "integrity": "sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==", - "dev": true - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true - }, - "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/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/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "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": "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/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "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": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "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/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": 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/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.4.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", - "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.341", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.341.tgz", - "integrity": "sha512-R4A8VfUBQY9WmAhuqY5tjHRf5fH2AAf6vqitBOE0y6u2PgHgqHSrhZmu78dIX3fVZtjqlwJNX1i2zwC3VpHtQQ==", - "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/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/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": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "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/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/expect": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", - "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/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/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "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/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-tsconfig": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", - "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", - "dev": true, - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "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-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": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "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": "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/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/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/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-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-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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-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": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "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/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/isomorphic-fetch": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", - "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", - "dependencies": { - "node-fetch": "^2.6.1", - "whatwg-fetch": "^3.4.1" - } - }, - "node_modules/isomorphic-fetch/node_modules/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "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.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.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.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", - "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==", - "dev": true, - "dependencies": { - "@jest/core": "^29.5.0", - "@jest/types": "^29.5.0", - "import-local": "^3.0.2", - "jest-cli": "^29.5.0" - }, - "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.5.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", - "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", - "dev": true, - "dependencies": { - "execa": "^5.0.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.5.0.tgz", - "integrity": "sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/expect": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.5.0", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.5.0", - "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-cli": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.5.0.tgz", - "integrity": "sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==", - "dev": true, - "dependencies": { - "@jest/core": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "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-config": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.5.0.tgz", - "integrity": "sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.5.0", - "@jest/types": "^29.5.0", - "babel-jest": "^29.5.0", - "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.5.0", - "jest-environment-node": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-runner": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.5.0", - "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-diff": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", - "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", - "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", - "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.5.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.5.0.tgz", - "integrity": "sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.5.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "jest-util": "^29.5.0", - "pretty-format": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz", - "integrity": "sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/fake-timers": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "jest-mock": "^29.5.0", - "jest-util": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", - "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.5.0.tgz", - "integrity": "sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.5.0", - "@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.4.3", - "jest-util": "^29.5.0", - "jest-worker": "^29.5.0", - "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.5.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz", - "integrity": "sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", - "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.5.0", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", - "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.5.0", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.5.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz", - "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.5.0", - "@types/node": "*", - "jest-util": "^29.5.0" - }, - "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.4.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", - "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.5.0.tgz", - "integrity": "sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "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.5.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz", - "integrity": "sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==", - "dev": true, - "dependencies": { - "jest-regex-util": "^29.4.3", - "jest-snapshot": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.5.0.tgz", - "integrity": "sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.5.0", - "@jest/environment": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.4.3", - "jest-environment-node": "^29.5.0", - "jest-haste-map": "^29.5.0", - "jest-leak-detector": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-resolve": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-util": "^29.5.0", - "jest-watcher": "^29.5.0", - "jest-worker": "^29.5.0", - "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-runtime": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.5.0.tgz", - "integrity": "sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/fake-timers": "^29.5.0", - "@jest/globals": "^29.5.0", - "@jest/source-map": "^29.4.3", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@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.5.0", - "jest-message-util": "^29.5.0", - "jest-mock": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.5.0.tgz", - "integrity": "sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==", - "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/traverse": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/babel__traverse": "^7.0.6", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.5.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.5.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "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.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", - "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.5.0", - "@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-validate": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.5.0.tgz", - "integrity": "sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.5.0", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "leven": "^3.1.0", - "pretty-format": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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-watcher": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.5.0.tgz", - "integrity": "sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.5.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz", - "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==", - "dev": true, - "dependencies": { - "@types/node": "*", - "jest-util": "^29.5.0", - "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/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/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/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/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==" - }, - "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/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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/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/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": ">= 8" - } - }, - "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": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.3.tgz", - "integrity": "sha512-KgUb15Oorc0NEKPbvfa0wRU+PItIEZmiv+pyAO2i0oTIVTJhlzMclU7w4RXWQrSOVH5ax/p/CkIO7KI4OyFJTQ==", - "funding": [ - "https://github.com/sponsors/broofa" - ], - "bin": { - "mime": "bin/cli.js" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "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/mkdirp": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", - "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", - "dev": true, - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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/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/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.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", - "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==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "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==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "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==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "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==", - "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" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "dev": true - }, - "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==", - "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==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "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/peggy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/peggy/-/peggy-3.0.2.tgz", - "integrity": "sha512-n7chtCbEoGYRwZZ0i/O3t1cPr6o+d9Xx4Zwy2LYfzv0vjchMBU0tO+qYYyvZloBPcgRgzYvALzGWHe609JjEpg==", - "dev": true, - "dependencies": { - "commander": "^10.0.0", - "source-map-generator": "0.8.0" - }, - "bin": { - "peggy": "bin/peggy.js" - }, - "engines": { - "node": ">=14" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "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==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/prettier": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", - "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", - "dev": true, - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/pretty-format": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", - "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.4.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "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_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pure-rand": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.1.tgz", - "integrity": "sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ] - }, - "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/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 - }, - "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==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-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==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "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, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "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": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "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": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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, - "engines": { - "node": ">=8" - } - }, - "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/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-generator": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/source-map-generator/-/source-map-generator-0.8.0.tgz", - "integrity": "sha512-psgxdGMwl5MZM9S3FWee4EgsEaIjahYV5AzGnwUvPhWeITz/j6rKpysQHlQ4USdxvINlb8lKfWGIXwfkrgtqkA==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "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/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/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "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==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "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": ">=8" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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/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, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "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-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": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/ts-jest": { - "version": "29.0.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.5.tgz", - "integrity": "sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA==", - "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.x", - "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" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "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==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "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/ts-matches": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-5.5.1.tgz", - "integrity": "sha512-UFYaKgfqlg9FROK7bdpYqFwG1CJvP4kOJdjXuWoqxo9jCmANoDw1GxkSCpJgoTeIiSTaTH5Qr1klSspb8c+ydg==" - }, - "node_modules/ts-morph": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-18.0.0.tgz", - "integrity": "sha512-Kg5u0mk19PIIe4islUI/HWRvm9bC1lHejK4S0oh1zaZ77TMZAEmQC0sHQYiu2RgCQFZKXz1fMVi/7nOOeirznA==", - "dev": true, - "dependencies": { - "@ts-morph/common": "~0.19.0", - "code-block-writer": "^12.0.0" - } - }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/ts-pegjs": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ts-pegjs/-/ts-pegjs-4.2.1.tgz", - "integrity": "sha512-mK/O2pu6lzWUeKpEMA/wsa0GdYblfjJI1y0s0GqH6xCTvugQDOWPJbm5rY6AHivpZICuXIriCb+a7Cflbdtc2w==", - "dev": true, - "dependencies": { - "prettier": "^2.8.8", - "ts-morph": "^18.0.0" - }, - "bin": { - "tspegjs": "dist/cli.mjs" - }, - "peerDependencies": { - "peggy": "^3.0.2" - } - }, - "node_modules/ts-pegjs/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": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/tsx": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.7.1.tgz", - "integrity": "sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==", - "dev": true, - "dependencies": { - "esbuild": "~0.19.10", - "get-tsconfig": "^4.7.2" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "node_modules/tsx/node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/tsx/node_modules/esbuild": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=12.20" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "browserslist-lint": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "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==", - "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" - } - }, - "node_modules/v8-to-istanbul/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/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": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-fetch": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", - "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" - }, - "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_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" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "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==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/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/wrap-ansi/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/wrap-ansi/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==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "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/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/yaml": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", - "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", - "engines": { - "node": ">= 14" - } - }, - "node_modules/yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", - "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/yargs/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/yargs/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/yargs/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==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } + "packages": {} } diff --git a/sdk/package/.gitignore b/sdk/package/.gitignore new file mode 100644 index 000000000..a7ca92b2d --- /dev/null +++ b/sdk/package/.gitignore @@ -0,0 +1,5 @@ +.vscode +dist/ +node_modules/ +lib/coverage +lib/test/output.ts \ No newline at end of file diff --git a/sdk/package/.npmignore b/sdk/package/.npmignore new file mode 100644 index 000000000..40b878db5 --- /dev/null +++ b/sdk/package/.npmignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/sdk/package/.prettierignore b/sdk/package/.prettierignore new file mode 100644 index 000000000..19b24bbe8 --- /dev/null +++ b/sdk/package/.prettierignore @@ -0,0 +1 @@ +/lib/exver/exver.ts \ No newline at end of file diff --git a/sdk/package/LICENSE b/sdk/package/LICENSE new file mode 100644 index 000000000..793257b96 --- /dev/null +++ b/sdk/package/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Start9 Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/README.md b/sdk/package/README.md similarity index 100% rename from sdk/README.md rename to sdk/package/README.md diff --git a/sdk/package/jest.config.js b/sdk/package/jest.config.js new file mode 100644 index 000000000..c38fa5062 --- /dev/null +++ b/sdk/package/jest.config.js @@ -0,0 +1,8 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: "ts-jest", + automock: false, + testEnvironment: "node", + rootDir: "./lib/", + modulePathIgnorePatterns: ["./dist/"], +} diff --git a/sdk/package/lib/StartSdk.ts b/sdk/package/lib/StartSdk.ts new file mode 100644 index 000000000..05cc003cb --- /dev/null +++ b/sdk/package/lib/StartSdk.ts @@ -0,0 +1,1410 @@ +import { + RequiredDefault, + Value, +} from "../../base/lib/actions/input/builder/value" +import { + InputSpec, + ExtractInputSpecType, + LazyBuild, +} from "../../base/lib/actions/input/builder/inputSpec" +import { + DefaultString, + ListValueSpecText, + Pattern, + RandomString, + UniqueBy, + ValueSpecDatetime, + ValueSpecText, +} from "../../base/lib/actions/input/inputSpecTypes" +import { Variants } from "../../base/lib/actions/input/builder/variants" +import { Action, Actions } from "../../base/lib/actions/setupActions" +import { + SyncOptions, + ServiceInterfaceId, + PackageId, + HealthReceipt, + ServiceInterfaceType, + Effects, +} from "../../base/lib/types" +import * as patterns from "../../base/lib/util/patterns" +import { BackupSync, Backups } from "./backup/Backups" +import { smtpInputSpec } from "../../base/lib/actions/input/inputSpecConstants" +import { Daemons } from "./mainFn/Daemons" +import { healthCheck, HealthCheckParams } from "./health/HealthCheck" +import { checkPortListening } from "./health/checkFns/checkPortListening" +import { checkWebUrl, runHealthScript } from "./health/checkFns" +import { List } from "../../base/lib/actions/input/builder/list" +import { Install, InstallFn } from "./inits/setupInstall" +import { SetupBackupsParams, setupBackups } from "./backup/setupBackups" +import { Uninstall, UninstallFn, setupUninstall } from "./inits/setupUninstall" +import { setupMain } from "./mainFn" +import { defaultTrigger } from "./trigger/defaultTrigger" +import { changeOnFirstSuccess, cooldownTrigger } from "./trigger" +import { + ServiceInterfacesReceipt, + UpdateServiceInterfaces, + setupServiceInterfaces, +} from "../../base/lib/interfaces/setupInterfaces" +import { successFailure } from "./trigger/successFailure" +import { MultiHost, Scheme } from "../../base/lib/interfaces/Host" +import { ServiceInterfaceBuilder } from "../../base/lib/interfaces/ServiceInterfaceBuilder" +import { GetSystemSmtp } from "./util" +import { nullIfEmpty } from "./util" +import { getServiceInterface, getServiceInterfaces } from "./util" +import { getStore } from "./store/getStore" +import { CommandOptions, MountOptions, SubContainer } from "./util/SubContainer" +import { splitCommand } from "./util" +import { Mounts } from "./mainFn/Mounts" +import { Dependency } from "../../base/lib/dependencies/Dependency" +import { setupDependencies } from "../../base/lib/dependencies/setupDependencies" +import * as T from "../../base/lib/types" +import { testTypeVersion } from "../../base/lib/exver" +import { ExposedStorePaths } from "./store/setupExposeStore" +import { + PathBuilder, + extractJsonPath, + pathBuilder, +} from "../../base/lib/util/PathBuilder" +import { + CheckDependencies, + checkDependencies, +} from "../../base/lib/dependencies/dependencies" +import { GetSslCertificate } from "./util" +import { VersionGraph } from "./version" +import { MaybeFn } from "../../base/lib/actions/setupActions" +import { GetInput } from "../../base/lib/actions/setupActions" +import { Run } from "../../base/lib/actions/setupActions" +import * as actions from "../../base/lib/actions" +import { setupInit } from "./inits/setupInit" + +export const SDKVersion = testTypeVersion("0.3.6") + +// prettier-ignore +type AnyNeverCond = + T extends [] ? Else : + T extends [never, ...Array] ? Then : + T extends [any, ...infer U] ? AnyNeverCond : + never + +export class StartSdk { + private constructor(readonly manifest: Manifest) {} + static of() { + return new StartSdk(null as never) + } + withManifest(manifest: Manifest) { + return new StartSdk(manifest) + } + withStore>() { + return new StartSdk(this.manifest) + } + + build(isReady: AnyNeverCond<[Manifest, Store], "Build not ready", true>) { + type NestedEffects = "subcontainer" | "store" | "action" + type InterfaceEffects = + | "getServiceInterface" + | "listServiceInterfaces" + | "exportServiceInterface" + | "clearServiceInterfaces" + | "bind" + | "getHostInfo" + | "getPrimaryUrl" + type MainUsedEffects = "setMainStatus" | "setHealth" + type CallbackEffects = "constRetry" | "clearCallbacks" + type AlreadyExposed = "getSslCertificate" | "getSystemSmtp" + + // prettier-ignore + type StartSdkEffectWrapper = { + [K in keyof Omit]: (effects: Effects, ...args: Parameters) => ReturnType + } + const startSdkEffectWrapper: StartSdkEffectWrapper = { + restart: (effects, ...args) => effects.restart(...args), + setDependencies: (effects, ...args) => effects.setDependencies(...args), + checkDependencies: (effects, ...args) => + effects.checkDependencies(...args), + mount: (effects, ...args) => effects.mount(...args), + getInstalledPackages: (effects, ...args) => + effects.getInstalledPackages(...args), + exposeForDependents: (effects, ...args) => + effects.exposeForDependents(...args), + getServicePortForward: (effects, ...args) => + effects.getServicePortForward(...args), + clearBindings: (effects, ...args) => effects.clearBindings(...args), + getContainerIp: (effects, ...args) => effects.getContainerIp(...args), + getSslKey: (effects, ...args) => effects.getSslKey(...args), + setDataVersion: (effects, ...args) => effects.setDataVersion(...args), + getDataVersion: (effects, ...args) => effects.getDataVersion(...args), + shutdown: (effects, ...args) => effects.shutdown(...args), + getDependencies: (effects, ...args) => effects.getDependencies(...args), + } + + return { + ...startSdkEffectWrapper, + action: { + run: actions.runAction, + request: actions.requestAction, + requestOwn: >( + effects: T.Effects, + request: actions.ActionRequest & { + replayId?: string + }, + ) => + actions.requestAction({ + effects, + request: { ...request, packageId: this.manifest.id }, + }), + }, + checkDependencies: checkDependencies as < + DependencyId extends keyof Manifest["dependencies"] & + PackageId = keyof Manifest["dependencies"] & PackageId, + >( + effects: Effects, + packageIds?: DependencyId[], + ) => Promise>, + serviceInterface: { + getOwn: (effects: E, id: ServiceInterfaceId) => + getServiceInterface(effects, { + id, + }), + get: ( + effects: E, + opts: { id: ServiceInterfaceId; packageId: PackageId }, + ) => getServiceInterface(effects, opts), + getAllOwn: (effects: E) => + getServiceInterfaces(effects, {}), + getAll: ( + effects: E, + opts: { packageId: PackageId }, + ) => getServiceInterfaces(effects, opts), + }, + + store: { + get: ( + effects: E, + packageId: string, + path: PathBuilder, + ) => + getStore(effects, path, { + packageId, + }), + getOwn: ( + effects: E, + path: PathBuilder, + ) => getStore(effects, path), + setOwn: >( + effects: E, + path: Path, + value: Path extends PathBuilder ? Value : never, + ) => + effects.store.set({ + value, + path: extractJsonPath(path), + }), + }, + + host: { + // static: (effects: Effects, id: string) => + // new StaticHost({ id, effects }), + // single: (effects: Effects, id: string) => + // new SingleHost({ id, effects }), + multi: (effects: Effects, id: string) => new MultiHost({ id, effects }), + }, + nullIfEmpty, + runCommand: async ( + effects: Effects, + image: { + id: keyof Manifest["images"] & T.ImageId + sharedRun?: boolean + }, + command: T.CommandType, + options: CommandOptions & { + mounts?: { path: string; options: MountOptions }[] + }, + name: string, + ): Promise<{ stdout: string | Buffer; stderr: string | Buffer }> => { + return runCommand(effects, image, command, options, name) + }, + /** + * TODO: rewrite this + * @description Use this function to create a static Action, including optional form input. + * + * By convention, each Action should receive its own file. + * + * @param id + * @param metaData + * @param fn + * @returns + * @example + * In this example, we create an Action that prints a name to the console. We present a user + * with a form for optionally entering a temp name. If no temp name is provided, we use the name + * from the underlying `inputSpec.yaml` file. If no name is there, we use "Unknown". Then, we return + * a message to the user informing them what happened. + * + * ``` + import { sdk } from '../sdk' + const { InputSpec, Value } = sdk + import { yamlFile } from '../file-models/inputSpec.yml' + + const input = InputSpec.of({ + nameToPrint: Value.text({ + name: 'Temp Name', + description: 'If no name is provided, the name from inputSpec will be used', + required: false, + }), + }) + + export const nameToLog = sdk.createAction( + // id + 'nameToLogs', + + // metadata + { + name: 'Name to Logs', + description: 'Prints "Hello [Name]" to the service logs.', + warning: null, + disabled: false, + input, + allowedStatuses: 'onlyRunning', + group: null, + }, + + // the execution function + async ({ effects, input }) => { + const name = + input.nameToPrint || (await yamlFile.read(effects))?.name || 'Unknown' + + console.info(`Hello ${name}`) + + return { + version: '0', + message: `"Hello ${name}" has been written to the service logs. Open your logs to view it.`, + value: name, + copyable: true, + qr: false, + } + }, + ) + * ``` + */ + Action: { + withInput: < + Id extends T.ActionId, + InputSpecType extends + | Record + | InputSpec + | InputSpec, + Type extends + ExtractInputSpecType = ExtractInputSpecType, + >( + id: Id, + metadata: MaybeFn>, + inputSpec: InputSpecType, + getInput: GetInput, + run: Run, + ) => Action.withInput(id, metadata, inputSpec, getInput, run), + withoutInput: ( + id: Id, + metadata: MaybeFn>, + run: Run<{}>, + ) => Action.withoutInput(id, metadata, run), + }, + inputSpecConstants: { smtpInputSpec }, + /** + * @description Use this function to create a service interface. + * @param effects + * @param options + * @example + * In this example, we create a standard web UI + * + * ``` + const ui = sdk.createInterface(effects, { + name: 'Web UI', + id: 'ui', + description: 'The primary web app for this service.', + type: 'ui', + hasPrimary: false, + masked: false, + schemeOverride: null, + username: null, + path: '', + search: {}, + }) + * ``` + */ + createInterface: ( + effects: Effects, + options: { + /** The human readable name of this service interface. */ + name: string + /** A unique ID for this service interface. */ + id: string + /** The human readable description. */ + description: string + /** Not available until StartOS v0.4.0. If true, forces the user to select one URL (i.e. .onion, .local, or IP address) as the primary URL. This is needed by some services to function properly. */ + hasPrimary: boolean + /** Affects how the interface appears to the user. One of: 'ui', 'api', 'p2p'. */ + type: ServiceInterfaceType + /** (optional) prepends the provided username to all URLs. */ + username: null | string + /** (optional) appends the provided path to all URLs. */ + path: string + /** (optional) appends the provided query params to all URLs. */ + search: Record + /** (optional) overrides the protocol prefix provided by the bind function. + * + * @example `ftp://` + */ + schemeOverride: { ssl: Scheme; noSsl: Scheme } | null + /** TODO Aiden how would someone include a password in the URL? Whether or not to mask the URLs on the screen, for example, when they contain a password */ + masked: boolean + }, + ) => new ServiceInterfaceBuilder({ ...options, effects }), + getSystemSmtp: (effects: E) => + new GetSystemSmtp(effects), + getSslCerificate: ( + effects: E, + hostnames: string[], + algorithm?: T.Algorithm, + ) => new GetSslCertificate(effects, hostnames, algorithm), + HealthCheck: { + of(o: HealthCheckParams) { + return healthCheck(o) + }, + }, + Dependency: { + /** + * @description Use this function to create a dependency for the service. + * @property {DependencyType} type + * @property {VersionRange} versionRange + * @property {string[]} healthChecks + */ + of(data: Dependency["data"]) { + return new Dependency({ ...data }) + }, + }, + healthCheck: { + checkPortListening, + checkWebUrl, + runHealthScript, + }, + patterns, + /** + * @description Use this function to list every Action offered by the service. Actions will be displayed in the provided order. + * + * By convention, each Action should receive its own file in the "actions" directory. + * @example + * + * ``` + import { sdk } from '../sdk' + import { config } from './config' + import { nameToLogs } from './nameToLogs' + + export const actions = sdk.Actions.of().addAction(config).addAction(nameToLogs) + * ``` + */ + Actions: Actions, + /** + * @description Use this function to determine which volumes are backed up when a user creates a backup, including advanced options. + * @example + * In this example, we back up the entire "main" volume and nothing else. + * + * ``` + export const { createBackup, restoreBackup } = sdk.setupBackups(sdk.Backups.addVolume('main')) + * ``` + * @example + * In this example, we back up the "main" and the "other" volume, but exclude hypothetical directory "excludedDir" from the "other". + * + * ``` + export const { createBackup, restoreBackup } = sdk.setupBackups(sdk.Backups + .addVolume('main') + .addVolume('other', { exclude: ['path/to/excludedDir'] }) + ) + * ``` + */ + setupBackups: (options: SetupBackupsParams) => + setupBackups(options), + /** + * @description Use this function to set dependency information. + * + * The function executes on service install, update, and inputSpec save. "input" will be of type `Input` for inputSpec save. It will be `null` for install and update. + * @example + * In this example, we create a static dependency on Hello World >=1.0.0:0, where Hello World must be running and passing its "webui" health check. + * + * ``` + export const setDependencies = sdk.setupDependencies( + async ({ effects, input }) => { + return { + 'hello-world': sdk.Dependency.of({ + type: 'running', + versionRange: VersionRange.parse('>=1.0.0:0'), + healthChecks: ['webui'], + }), + } + }, + ) + * ``` + * @example + * In this example, we create a conditional dependency on Hello World based on a hypothetical "needsWorld" boolean in the store. + * + * ``` + export const setDependencies = sdk.setupDependencies( + async ({ effects }) => { + if (sdk.store.getOwn(sdk.StorePath.needsWorld).const()) { + return { + 'hello-world': sdk.Dependency.of({ + type: 'running', + versionRange: VersionRange.parse('>=1.0.0:0'), + healthChecks: ['webui'], + }), + } + } + return {} + }, + ) + * ``` + */ + setupDependencies: setupDependencies, + setupInit: setupInit, + /** + * @description Use this function to execute arbitrary logic *once*, on initial install only. + * @example + * In the this example, we bootstrap our Store with a random, 16-char admin password. + * + * ``` + const install = sdk.setupInstall(async ({ effects }) => { + await sdk.store.setOwn( + effects, + sdk.StorePath.adminPassword, + utils.getDefaultString({ + charset: 'a-z,A-Z,1-9,!,@,$,%,&,', + len: 16, + }), + ) + }) + * ``` + */ + setupInstall: (fn: InstallFn) => Install.of(fn), + /** + * @description Use this function to determine how this service will be hosted and served. The function executes on service install, service update, and inputSpec save. + * + * "input" will be of type `Input` for inputSpec save. It will be `null` for install and update. + * + * To learn about creating multi-hosts and interfaces, check out the {@link https://docs.start9.com/packaging-guide/learn/interfaces documentation}. + * @param inputSpec - The inputSpec spec of this service as exported from /inputSpec/spec. + * @param fn - an async function that returns an array of interface receipts. The function always has access to `effects`; it has access to `input` only after inputSpec save, otherwise `input` will be null. + * @example + * In this example, we create two UIs from one multi-host, and one API from another multi-host. + * + * ``` + export const setInterfaces = sdk.setupInterfaces( + inputSpecSpec, + async ({ effects, input }) => { + // ** UI multi-host ** + const uiMulti = sdk.host.multi(effects, 'ui-multi') + const uiMultiOrigin = await uiMulti.bindPort(80, { + protocol: 'http', + }) + // Primary UI + const primaryUi = sdk.createInterface(effects, { + name: 'Primary UI', + id: 'primary-ui', + description: 'The primary web app for this service.', + type: 'ui', + hasPrimary: false, + masked: false, + schemeOverride: null, + username: null, + path: '', + search: {}, + }) + // Admin UI + const adminUi = sdk.createInterface(effects, { + name: 'Admin UI', + id: 'admin-ui', + description: 'The admin web app for this service.', + type: 'ui', + hasPrimary: false, + masked: false, + schemeOverride: null, + username: null, + path: '/admin', + search: {}, + }) + // UI receipt + const uiReceipt = await uiMultiOrigin.export([primaryUi, adminUi]) + + // ** API multi-host ** + const apiMulti = sdk.host.multi(effects, 'api-multi') + const apiMultiOrigin = await apiMulti.bindPort(5959, { + protocol: 'http', + }) + // API + const api = sdk.createInterface(effects, { + name: 'Admin API', + id: 'api', + description: 'The advanced API for this service.', + type: 'api', + hasPrimary: false, + masked: false, + schemeOverride: null, + username: null, + path: '', + search: {}, + }) + // API receipt + const apiReceipt = await apiMultiOrigin.export([api]) + + // ** Return receipts ** + return [uiReceipt, apiReceipt] + }, + ) + * ``` + */ + setupInterfaces: setupServiceInterfaces, + setupMain: ( + fn: (o: { + effects: Effects + started(onTerm: () => PromiseLike): PromiseLike + }) => Promise>, + ) => setupMain(fn), + /** + * @description Use this function to determine which information to expose to the UI in the "Properties" section. + * + * Values can be obtained from anywhere: the Store, the upstream service, or another service. + * @example + * In this example, we retrieve the admin password from the Store and expose it, masked and copyable, to + * the UI as "Admin Password". + * + * ``` + export const properties = sdk.setupProperties(async ({ effects }) => { + const store = await sdk.store.getOwn(effects, sdk.StorePath).once() + + return { + 'Admin Password': { + type: 'string', + value: store.adminPassword, + description: 'Used for logging into the admin UI', + copyable: true, + masked: true, + qr: false, + }, + } + }) + * ``` + */ + setupProperties: + ( + fn: (options: { effects: Effects }) => Promise, + ): T.ExpectedExports.properties => + (options) => + fn(options).then(nullifyProperties), + /** + * Use this function to execute arbitrary logic *once*, on uninstall only. Most services will not use this. + */ + setupUninstall: (fn: UninstallFn) => + setupUninstall(fn), + trigger: { + defaultTrigger, + cooldownTrigger, + changeOnFirstSuccess, + successFailure, + }, + Mounts: { + of() { + return Mounts.of() + }, + }, + Backups: { + volumes: ( + ...volumeNames: Array + ) => Backups.withVolumes(...volumeNames), + addSets: ( + ...options: BackupSync[] + ) => Backups.withSyncs(...options), + withOptions: (options?: Partial) => + Backups.withOptions(options), + }, + InputSpec: { + /** + * @description Use this function to define the inputSpec specification that will ultimately present to the user as validated form inputs. + * + * Most form controls are supported, including text, textarea, number, toggle, select, multiselect, list, color, datetime, object (sub form), and union (conditional sub form). + * @example + * In this example, we define a inputSpec form with two value: name and makePublic. + * + * ``` + import { sdk } from '../sdk' + const { InputSpec, Value } = sdk + + export const inputSpecSpec = InputSpec.of({ + name: Value.text({ + name: 'Name', + description: + 'When you launch the Hello World UI, it will display "Hello [Name]"', + required: { default: 'World' }, + }), + makePublic: Value.toggle({ + name: 'Make Public', + description: 'Whether or not to expose the service to the network', + default: false, + }), + }) + * ``` + */ + of: < + Spec extends Record | Value>, + >( + spec: Spec, + ) => InputSpec.of(spec), + }, + Daemons: { + of(inputSpec: { + effects: Effects + started: (onTerm: () => PromiseLike) => PromiseLike + healthReceipts: HealthReceipt[] + }) { + return Daemons.of(inputSpec) + }, + }, + List: { + /** + * @description Create a list of text inputs. + * @param a - attributes of the list itself. + * @param aSpec - attributes describing each member of the list. + */ + text: List.text, + /** + * @description Create a list of objects. + * @param a - attributes of the list itself. + * @param aSpec - attributes describing each member of the list. + */ + obj: >( + a: { + name: string + description?: string | null + /** Presents a warning before adding/removing/editing a list item. */ + warning?: string | null + default?: [] + minLength?: number | null + maxLength?: number | null + }, + aSpec: { + spec: InputSpec + /** + * @description The ID of a required field on the inner object whose value will be used to display items in the list. + * @example + * In this example, we use the value of the `label` field to display members of the list. + * + * ``` + spec: InputSpec.of({ + label: Value.text({ + name: 'Label', + required: false, + }) + }) + displayAs: 'label', + uniqueBy: null, + * ``` + * + */ + displayAs?: null | string + /** + * @description The ID(s) of required fields on the inner object whose value(s) will be used to enforce uniqueness in the list. + * @example + * In this example, we use the `label` field to enforce uniqueness, meaning the label field must be unique from other entries. + * + * ``` + spec: InputSpec.of({ + label: Value.text({ + name: 'Label', + required: { default: null }, + }) + pubkey: Value.text({ + name: 'Pubkey', + required: { default: null }, + }) + }) + displayAs: 'label', + uniqueBy: 'label', + * ``` + * @example + * In this example, we use the `label` field AND the `pubkey` field to enforce uniqueness, meaning both these fields must be unique from other entries. + * + * ``` + spec: InputSpec.of({ + label: Value.text({ + name: 'Label', + required: { default: null }, + }) + pubkey: Value.text({ + name: 'Pubkey', + required: { default: null }, + }) + }) + displayAs: 'label', + uniqueBy: { all: ['label', 'pubkey'] }, + * ``` + */ + uniqueBy?: null | UniqueBy + }, + ) => List.obj(a, aSpec), + /** + * @description Create a list of dynamic text inputs. + * @param a - attributes of the list itself. + * @param aSpec - attributes describing each member of the list. + */ + dynamicText: ( + getA: LazyBuild< + Store, + { + name: string + description?: string | null + warning?: string | null + default?: string[] + minLength?: number | null + maxLength?: number | null + disabled?: false | string + generate?: null | RandomString + spec: { + masked?: boolean + placeholder?: string | null + minLength?: number | null + maxLength?: number | null + patterns: Pattern[] + inputmode?: ListValueSpecText["inputmode"] + } + } + >, + ) => List.dynamicText(getA), + }, + StorePath: pathBuilder(), + Value: { + /** + * @description Displays a boolean toggle to enable/disable + * @example + * ``` + toggleExample: Value.toggle({ + // required + name: 'Toggle Example', + default: true, + + // optional + description: null, + warning: null, + immutable: false, + }), + * ``` + */ + toggle: Value.toggle, + /** + * @description Displays a text input field + * @example + * ``` + textExample: Value.text({ + // required + name: 'Text Example', + required: false, + + // optional + description: null, + placeholder: null, + warning: null, + generate: null, + inputmode: 'text', + masked: false, + minLength: null, + maxLength: null, + patterns: [], + immutable: false, + }), + * ``` + */ + text: Value.text, + /** + * @description Displays a large textarea field for long form entry. + * @example + * ``` + textareaExample: Value.textarea({ + // required + name: 'Textarea Example', + required: false, + + // optional + description: null, + placeholder: null, + warning: null, + minLength: null, + maxLength: null, + immutable: false, + }), + * ``` + */ + textarea: Value.textarea, + /** + * @description Displays a number input field + * @example + * ``` + numberExample: Value.number({ + // required + name: 'Number Example', + required: false, + integer: true, + + // optional + description: null, + placeholder: null, + warning: null, + min: null, + max: null, + immutable: false, + step: null, + units: null, + }), + * ``` + */ + number: Value.number, + /** + * @description Displays a browser-native color selector. + * @example + * ``` + colorExample: Value.color({ + // required + name: 'Color Example', + required: false, + + // optional + description: null, + warning: null, + immutable: false, + }), + * ``` + */ + color: Value.color, + /** + * @description Displays a browser-native date/time selector. + * @example + * ``` + datetimeExample: Value.datetime({ + // required + name: 'Datetime Example', + required: false, + + // optional + description: null, + warning: null, + immutable: false, + inputmode: 'datetime-local', + min: null, + max: null, + }), + * ``` + */ + datetime: Value.datetime, + /** + * @description Displays a select modal with radio buttons, allowing for a single selection. + * @example + * ``` + selectExample: Value.select({ + // required + name: 'Select Example', + required: false, + values: { + radio1: 'Radio 1', + radio2: 'Radio 2', + }, + + // optional + description: null, + warning: null, + immutable: false, + disabled: false, + }), + * ``` + */ + select: Value.select, + /** + * @description Displays a select modal with checkboxes, allowing for multiple selections. + * @example + * ``` + multiselectExample: Value.multiselect({ + // required + name: 'Multiselect Example', + values: { + option1: 'Option 1', + option2: 'Option 2', + }, + default: [], + + // optional + description: null, + warning: null, + immutable: false, + disabled: false, + minlength: null, + maxLength: null, + }), + * ``` + */ + multiselect: Value.multiselect, + /** + * @description Display a collapsable grouping of additional fields, a "sub form". The second value is the inputSpec spec for the sub form. + * @example + * ``` + objectExample: Value.object( + { + // required + name: 'Object Example', + + // optional + description: null, + warning: null, + }, + InputSpec.of({}), + ), + * ``` + */ + object: Value.object, + /** + * @description Displays a dropdown, allowing for a single selection. Depending on the selection, a different object ("sub form") is presented. + * @example + * ``` + unionExample: Value.union( + { + // required + name: 'Union Example', + required: false, + + // optional + description: null, + warning: null, + disabled: false, + immutable: false, + }, + Variants.of({ + option1: { + name: 'Option 1', + spec: InputSpec.of({}), + }, + option2: { + name: 'Option 2', + spec: InputSpec.of({}), + }, + }), + ), + * ``` + */ + union: Value.union, + /** + * @description Presents an interface to add/remove/edit items in a list. + * @example + * In this example, we create a list of text inputs. + * + * ``` + listExampleText: Value.list( + List.text( + { + // required + name: 'Text List', + + // optional + description: null, + warning: null, + default: [], + minLength: null, + maxLength: null, + }, + { + // required + patterns: [], + + // optional + placeholder: null, + generate: null, + inputmode: 'url', + masked: false, + minLength: null, + maxLength: null, + }, + ), + ), + * ``` + * @example + * In this example, we create a list of objects. + * + * ``` + listExampleObject: Value.list( + List.obj( + { + // required + name: 'Object List', + + // optional + description: null, + warning: null, + default: [], + minLength: null, + maxLength: null, + }, + { + // required + spec: InputSpec.of({}), + + // optional + displayAs: null, + uniqueBy: null, + }, + ), + ), + * ``` + */ + list: Value.list, + dynamicToggle: ( + a: LazyBuild< + Store, + { + name: string + description?: string | null + /** Presents a warning prompt before permitting the value to change. */ + warning?: string | null + default: boolean + disabled?: false | string + } + >, + ) => Value.dynamicToggle(a), + dynamicText: ( + getA: LazyBuild< + Store, + { + name: string + description?: string | null + /** Presents a warning prompt before permitting the value to change. */ + warning?: string | null + /** + * @description Determines if the field is required. If so, optionally provide a default value. + * @type { false | { default: string | RandomString | null } } + * @example required: false + * @example required: { default: null } + * @example required: { default: 'World' } + * @example required: { default: { charset: 'abcdefg', len: 16 } } + */ + required: RequiredDefault + /** + * @description Mask (aka camouflage) text input with dots: ● ● ● + * @default false + */ + masked?: boolean + placeholder?: string | null + minLength?: number | null + maxLength?: number | null + /** + * @description A list of regular expressions to which the text must conform to pass validation. A human readable description is provided in case the validation fails. + * @default [] + * @example + * ``` + [ + { + regex: "[a-z]", + description: "May only contain lower case letters from the English alphabet." + } + ] + * ``` + */ + patterns?: Pattern[] + /** + * @description Informs the browser how to behave and which keyboard to display on mobile + * @default "text" + */ + inputmode?: ValueSpecText["inputmode"] + /** + * @description Displays a button that will generate a random string according to the provided charset and len attributes. + */ + generate?: null | RandomString + } + >, + ) => Value.dynamicText(getA), + dynamicTextarea: ( + getA: LazyBuild< + Store, + { + name: string + description?: string | null + /** Presents a warning prompt before permitting the value to change. */ + warning?: string | null + /** + * @description Unlike other "required" fields, for textarea this is a simple boolean. + */ + required: boolean + minLength?: number | null + maxLength?: number | null + placeholder?: string | null + disabled?: false | string + generate?: null | RandomString + } + >, + ) => Value.dynamicTextarea(getA), + dynamicNumber: ( + getA: LazyBuild< + Store, + { + name: string + description?: string | null + /** Presents a warning prompt before permitting the value to change. */ + warning?: string | null + /** + * @description Determines if the field is required. If so, optionally provide a default value. + * @type { false | { default: number | null } } + * @example required: false + * @example required: { default: null } + * @example required: { default: 7 } + */ + required: RequiredDefault + min?: number | null + max?: number | null + /** + * @description How much does the number increase/decrease when using the arrows provided by the browser. + * @default 1 + */ + step?: number | null + /** + * @description Requires the number to be an integer. + */ + integer: boolean + /** + * @description Optionally display units to the right of the input box. + */ + units?: string | null + placeholder?: string | null + disabled?: false | string + } + >, + ) => Value.dynamicNumber(getA), + dynamicColor: ( + getA: LazyBuild< + Store, + { + name: string + description?: string | null + /** Presents a warning prompt before permitting the value to change. */ + warning?: string | null + /** + * @description Determines if the field is required. If so, optionally provide a default value. + * @type { false | { default: string | null } } + * @example required: false + * @example required: { default: null } + * @example required: { default: 'ffffff' } + */ + required: RequiredDefault + disabled?: false | string + } + >, + ) => Value.dynamicColor(getA), + dynamicDatetime: ( + getA: LazyBuild< + Store, + { + name: string + description?: string | null + /** Presents a warning prompt before permitting the value to change. */ + warning?: string | null + /** + * @description Determines if the field is required. If so, optionally provide a default value. + * @type { false | { default: string | null } } + * @example required: false + * @example required: { default: null } + * @example required: { default: '1985-12-16 18:00:00.000' } + */ + required: RequiredDefault + /** + * @description Informs the browser how to behave and which date/time component to display. + * @default "datetime-local" + */ + inputmode?: ValueSpecDatetime["inputmode"] + min?: string | null + max?: string | null + disabled?: false | string + } + >, + ) => Value.dynamicDatetime(getA), + dynamicSelect: ( + getA: LazyBuild< + Store, + { + name: string + description?: string | null + /** Presents a warning prompt before permitting the value to change. */ + warning?: string | null + /** + * @description Determines if the field is required. If so, optionally provide a default value from the list of values. + * @type { false | { default: string | null } } + * @example required: false + * @example required: { default: null } + * @example required: { default: 'radio1' } + */ + required: RequiredDefault + /** + * @description A mapping of unique radio options to their human readable display format. + * @example + * ``` + { + radio1: "Radio 1" + radio2: "Radio 2" + radio3: "Radio 3" + } + * ``` + */ + values: Record + /** + * @options + * - false - The field can be modified. + * - string - The field cannot be modified. The provided text explains why. + * - string[] - The field can be modified, but the values contained in the array cannot be selected. + * @default false + */ + disabled?: false | string | string[] + } + >, + ) => Value.dynamicSelect(getA), + dynamicMultiselect: ( + getA: LazyBuild< + Store, + { + name: string + description?: string | null + /** Presents a warning prompt before permitting the value to change. */ + warning?: string | null + /** + * @description A simple list of which options should be checked by default. + */ + default: string[] + /** + * @description A mapping of checkbox options to their human readable display format. + * @example + * ``` + { + option1: "Option 1" + option2: "Option 2" + option3: "Option 3" + } + * ``` + */ + values: Record + minLength?: number | null + maxLength?: number | null + /** + * @options + * - false - The field can be modified. + * - string - The field cannot be modified. The provided text explains why. + * - string[] - The field can be modified, but the values contained in the array cannot be selected. + * @default false + */ + disabled?: false | string | string[] + } + >, + ) => Value.dynamicMultiselect(getA), + filteredUnion: < + Required extends RequiredDefault, + Type extends Record, + >( + getDisabledFn: LazyBuild, + a: { + name: string + description?: string | null + warning?: string | null + required: Required + }, + aVariants: Variants | Variants, + ) => + Value.filteredUnion( + getDisabledFn, + a, + aVariants, + ), + + dynamicUnion: < + Required extends RequiredDefault, + Type extends Record, + >( + getA: LazyBuild< + Store, + { + name: string + description?: string | null + /** Presents a warning prompt before permitting the value to change. */ + warning?: string | null + /** + * @description Determines if the field is required. If so, optionally provide a default value from the list of variants. + * @type { false | { default: string | null } } + * @example required: false + * @example required: { default: null } + * @example required: { default: 'variant1' } + */ + required: Required + /** + * @options + * - false - The field can be modified. + * - string - The field cannot be modified. The provided text explains why. + * - string[] - The field can be modified, but the values contained in the array cannot be selected. + * @default false + */ + disabled: false | string | string[] + } + >, + aVariants: Variants | Variants, + ) => Value.dynamicUnion(getA, aVariants), + }, + Variants: { + of: < + VariantValues extends { + [K in string]: { + name: string + spec: InputSpec + } + }, + >( + a: VariantValues, + ) => Variants.of(a), + }, + } + } +} + +export async function runCommand( + effects: Effects, + image: { id: keyof Manifest["images"] & T.ImageId; sharedRun?: boolean }, + command: string | [string, ...string[]], + options: CommandOptions & { + mounts?: { path: string; options: MountOptions }[] + }, + name: string, +): Promise<{ stdout: string | Buffer; stderr: string | Buffer }> { + const commands = splitCommand(command) + return SubContainer.with( + effects, + image, + options.mounts || [], + name, + (subcontainer) => subcontainer.exec(commands), + ) +} +function nullifyProperties(value: T.SdkPropertiesReturn): T.PropertiesReturn { + return Object.fromEntries( + Object.entries(value).map(([k, v]) => [k, nullifyProperties_(v)]), + ) +} +function nullifyProperties_(value: T.SdkPropertiesValue): T.PropertiesValue { + if (value.type === "string") { + return { + description: null, + copyable: null, + masked: null, + qr: null, + ...value, + } + } + return { + description: null, + ...value, + value: Object.fromEntries( + Object.entries(value.value).map(([k, v]) => [k, nullifyProperties_(v)]), + ), + } +} diff --git a/sdk/package/lib/backup/Backups.ts b/sdk/package/lib/backup/Backups.ts new file mode 100644 index 000000000..8d8f03295 --- /dev/null +++ b/sdk/package/lib/backup/Backups.ts @@ -0,0 +1,208 @@ +import * as T from "../../../base/lib/types" +import * as child_process from "child_process" +import { asError } from "../util" + +export const DEFAULT_OPTIONS: T.SyncOptions = { + delete: true, + exclude: [], +} +export type BackupSync = { + dataPath: `/media/startos/volumes/${Volumes}/${string}` + backupPath: `/media/startos/backup/${string}` + options?: Partial + backupOptions?: Partial + restoreOptions?: Partial +} +/** + * This utility simplifies the volume backup process. + * ```ts + * export const { createBackup, restoreBackup } = Backups.volumes("main").build(); + * ``` + * + * Changing the options of the rsync, (ie excludes) use either + * ```ts + * Backups.volumes("main").set_options({exclude: ['bigdata/']}).volumes('excludedVolume').build() + * // or + * Backups.with_options({exclude: ['bigdata/']}).volumes('excludedVolume').build() + * ``` + * + * Using the more fine control, using the addSets for more control + * ```ts + * Backups.addSets({ + * srcVolume: 'main', srcPath:'smallData/', dstPath: 'main/smallData/', dstVolume: : Backups.BACKUP + * }, { + * srcVolume: 'main', srcPath:'bigData/', dstPath: 'main/bigData/', dstVolume: : Backups.BACKUP, options: {exclude:['bigData/excludeThis']}} + * ).build()q + * ``` + */ +export class Backups { + private constructor( + private options = DEFAULT_OPTIONS, + private restoreOptions: Partial = {}, + private backupOptions: Partial = {}, + private backupSet = [] as BackupSync[], + ) {} + + static withVolumes( + ...volumeNames: Array + ): Backups { + return Backups.withSyncs( + ...volumeNames.map((srcVolume) => ({ + dataPath: `/media/startos/volumes/${srcVolume}/` as const, + backupPath: `/media/startos/backup/${srcVolume}/` as const, + })), + ) + } + + static withSyncs( + ...syncs: BackupSync[] + ) { + return syncs.reduce((acc, x) => acc.addSync(x), new Backups()) + } + + static withOptions( + options?: Partial, + ) { + return new Backups({ ...DEFAULT_OPTIONS, ...options }) + } + + setOptions(options?: Partial) { + this.options = { + ...this.options, + ...options, + } + return this + } + + setBackupOptions(options?: Partial) { + this.backupOptions = { + ...this.backupOptions, + ...options, + } + return this + } + + setRestoreOptions(options?: Partial) { + this.restoreOptions = { + ...this.restoreOptions, + ...options, + } + return this + } + + addVolume( + volume: M["volumes"][number], + options?: Partial<{ + options: T.SyncOptions + backupOptions: T.SyncOptions + restoreOptions: T.SyncOptions + }>, + ) { + return this.addSync({ + dataPath: `/media/startos/volumes/${volume}/` as const, + backupPath: `/media/startos/backup/${volume}/` as const, + ...options, + }) + } + addSync(sync: BackupSync) { + this.backupSet.push({ + ...sync, + options: { ...this.options, ...sync.options }, + }) + return this + } + + async createBackup() { + for (const item of this.backupSet) { + const rsyncResults = await runRsync({ + srcPath: item.dataPath, + dstPath: item.backupPath, + options: { + ...this.options, + ...this.backupOptions, + ...item.options, + ...item.backupOptions, + }, + }) + await rsyncResults.wait() + } + return + } + + async restoreBackup() { + for (const item of this.backupSet) { + const rsyncResults = await runRsync({ + srcPath: item.backupPath, + dstPath: item.dataPath, + options: { + ...this.options, + ...this.backupOptions, + ...item.options, + ...item.backupOptions, + }, + }) + await rsyncResults.wait() + } + return + } +} + +async function runRsync(rsyncOptions: { + srcPath: string + dstPath: string + options: T.SyncOptions +}): Promise<{ + id: () => Promise + wait: () => Promise + progress: () => Promise +}> { + const { srcPath, dstPath, options } = rsyncOptions + + const command = "rsync" + const args: string[] = [] + if (options.delete) { + args.push("--delete") + } + for (const exclude of options.exclude) { + args.push(`--exclude=${exclude}`) + } + args.push("-actAXH") + args.push("--info=progress2") + args.push("--no-inc-recursive") + args.push(srcPath) + args.push(dstPath) + const spawned = child_process.spawn(command, args, { detached: true }) + let percentage = 0.0 + spawned.stdout.on("data", (data: unknown) => { + const lines = String(data).replace("\r", "\n").split("\n") + for (const line of lines) { + const parsed = /$([0-9.]+)%/.exec(line)?.[1] + if (!parsed) continue + percentage = Number.parseFloat(parsed) + } + }) + + spawned.stderr.on("data", (data: unknown) => { + console.error(`Backups.runAsync`, asError(data)) + }) + + const id = async () => { + const pid = spawned.pid + if (pid === undefined) { + throw new Error("rsync process has no pid") + } + return String(pid) + } + const waitPromise = new Promise((resolve, reject) => { + spawned.on("exit", (code: any) => { + if (code === 0) { + resolve(null) + } else { + reject(new Error(`rsync exited with code ${code}`)) + } + }) + }) + const wait = () => waitPromise + const progress = () => Promise.resolve(percentage) + return { id, wait, progress } +} diff --git a/sdk/lib/backup/index.ts b/sdk/package/lib/backup/index.ts similarity index 97% rename from sdk/lib/backup/index.ts rename to sdk/package/lib/backup/index.ts index fe9cd8569..1e7995252 100644 --- a/sdk/lib/backup/index.ts +++ b/sdk/package/lib/backup/index.ts @@ -1,3 +1,2 @@ import "./Backups" - import "./setupBackups" diff --git a/sdk/package/lib/backup/setupBackups.ts b/sdk/package/lib/backup/setupBackups.ts new file mode 100644 index 000000000..b9654824b --- /dev/null +++ b/sdk/package/lib/backup/setupBackups.ts @@ -0,0 +1,39 @@ +import { Backups } from "./Backups" +import * as T from "../../../base/lib/types" +import { _ } from "../util" + +export type SetupBackupsParams = + | M["volumes"][number][] + | ((_: { effects: T.Effects }) => Promise>) + +type SetupBackupsRes = { + createBackup: T.ExpectedExports.createBackup + restoreBackup: T.ExpectedExports.restoreBackup +} + +export function setupBackups( + options: SetupBackupsParams, +) { + let backupsFactory: (_: { effects: T.Effects }) => Promise> + if (options instanceof Function) { + backupsFactory = options + } else { + backupsFactory = async () => Backups.withVolumes(...options) + } + const answer: { + createBackup: T.ExpectedExports.createBackup + restoreBackup: T.ExpectedExports.restoreBackup + } = { + get createBackup() { + return (async (options) => { + return (await backupsFactory(options)).createBackup() + }) as T.ExpectedExports.createBackup + }, + get restoreBackup() { + return (async (options) => { + return (await backupsFactory(options)).restoreBackup() + }) as T.ExpectedExports.restoreBackup + }, + } + return answer +} diff --git a/sdk/lib/health/HealthCheck.ts b/sdk/package/lib/health/HealthCheck.ts similarity index 88% rename from sdk/lib/health/HealthCheck.ts rename to sdk/package/lib/health/HealthCheck.ts index e007c4ea2..05c1a214a 100644 --- a/sdk/lib/health/HealthCheck.ts +++ b/sdk/package/lib/health/HealthCheck.ts @@ -1,14 +1,10 @@ -import { Effects } from "../types" +import { Effects, HealthReceipt } from "../../../base/lib/types" import { HealthCheckResult } from "./checkFns/HealthCheckResult" -import { HealthReceipt } from "./HealthReceipt" import { Trigger } from "../trigger" import { TriggerInput } from "../trigger/TriggerInput" import { defaultTrigger } from "../trigger/defaultTrigger" -import { once } from "../util/once" -import { SubContainer } from "../util/SubContainer" +import { once, asError } from "../util" import { object, unknown } from "ts-matches" -import * as T from "../types" -import { asError } from "../util/asError" export type HealthCheckParams = { effects: Effects diff --git a/sdk/lib/health/checkFns/HealthCheckResult.ts b/sdk/package/lib/health/checkFns/HealthCheckResult.ts similarity index 63% rename from sdk/lib/health/checkFns/HealthCheckResult.ts rename to sdk/package/lib/health/checkFns/HealthCheckResult.ts index ba2468488..92d4afddf 100644 --- a/sdk/lib/health/checkFns/HealthCheckResult.ts +++ b/sdk/package/lib/health/checkFns/HealthCheckResult.ts @@ -1,3 +1,3 @@ -import { T } from "../.." +import { T } from "../../../../base/lib" export type HealthCheckResult = Omit diff --git a/sdk/lib/health/checkFns/checkPortListening.ts b/sdk/package/lib/health/checkFns/checkPortListening.ts similarity index 94% rename from sdk/lib/health/checkFns/checkPortListening.ts rename to sdk/package/lib/health/checkFns/checkPortListening.ts index 94d0becc0..e745bce4f 100644 --- a/sdk/lib/health/checkFns/checkPortListening.ts +++ b/sdk/package/lib/health/checkFns/checkPortListening.ts @@ -1,5 +1,5 @@ -import { Effects } from "../../types" -import { stringFromStdErrOut } from "../../util/stringFromStdErrOut" +import { Effects } from "../../../../base/lib/types" +import { stringFromStdErrOut } from "../../util" import { HealthCheckResult } from "./HealthCheckResult" import { promisify } from "node:util" diff --git a/sdk/lib/health/checkFns/checkWebUrl.ts b/sdk/package/lib/health/checkFns/checkWebUrl.ts similarity index 90% rename from sdk/lib/health/checkFns/checkWebUrl.ts rename to sdk/package/lib/health/checkFns/checkWebUrl.ts index 042115211..e04ee7531 100644 --- a/sdk/lib/health/checkFns/checkWebUrl.ts +++ b/sdk/package/lib/health/checkFns/checkWebUrl.ts @@ -1,5 +1,5 @@ -import { Effects } from "../../types" -import { asError } from "../../util/asError" +import { Effects } from "../../../../base/lib/types" +import { asError } from "../../util" import { HealthCheckResult } from "./HealthCheckResult" import { timeoutPromise } from "./index" import "isomorphic-fetch" diff --git a/sdk/lib/health/checkFns/index.ts b/sdk/package/lib/health/checkFns/index.ts similarity index 100% rename from sdk/lib/health/checkFns/index.ts rename to sdk/package/lib/health/checkFns/index.ts diff --git a/sdk/lib/health/checkFns/runHealthScript.ts b/sdk/package/lib/health/checkFns/runHealthScript.ts similarity index 91% rename from sdk/lib/health/checkFns/runHealthScript.ts rename to sdk/package/lib/health/checkFns/runHealthScript.ts index 4bac211a9..7ecd1ad75 100644 --- a/sdk/lib/health/checkFns/runHealthScript.ts +++ b/sdk/package/lib/health/checkFns/runHealthScript.ts @@ -1,8 +1,6 @@ -import { Effects } from "../../types" -import { SubContainer } from "../../util/SubContainer" -import { stringFromStdErrOut } from "../../util/stringFromStdErrOut" import { HealthCheckResult } from "./HealthCheckResult" import { timeoutPromise } from "./index" +import { SubContainer } from "../../util/SubContainer" /** * Running a health script, is used when we want to have a simple diff --git a/sdk/package/lib/health/index.ts b/sdk/package/lib/health/index.ts new file mode 100644 index 000000000..b969037a5 --- /dev/null +++ b/sdk/package/lib/health/index.ts @@ -0,0 +1 @@ +import "./checkFns" diff --git a/sdk/lib/index.ts b/sdk/package/lib/index.ts similarity index 58% rename from sdk/lib/index.ts rename to sdk/package/lib/index.ts index a4caf9c01..a83e35745 100644 --- a/sdk/lib/index.ts +++ b/sdk/package/lib/index.ts @@ -1,32 +1,48 @@ +import { + S9pk, + Version, + VersionRange, + ExtendedVersion, + inputSpec, + ISB, + IST, + types, + T, + matches, + utils, +} from "../../base/lib" + +export { + S9pk, + Version, + VersionRange, + ExtendedVersion, + inputSpec, + ISB, + IST, + types, + T, + matches, + utils, +} export { Daemons } from "./mainFn/Daemons" export { SubContainer } from "./util/SubContainer" export { StartSdk } from "./StartSdk" export { setupManifest } from "./manifest/setupManifest" export { FileHelper } from "./util/fileHelper" export { setupExposeStore } from "./store/setupExposeStore" -export { pathBuilder } from "./store/PathBuilder" -export { S9pk } from "./s9pk" -export { VersionRange, ExtendedVersion, Version } from "./exver" +export { pathBuilder } from "../../base/lib/util/PathBuilder" -export * as actions from "./actions" +export * as actions from "../../base/lib/actions" export * as backup from "./backup" -export * as config from "./config" -export * as CB from "./config/builder" -export * as CT from "./config/configTypes" -export * as dependencyConfig from "./dependencies" export * as daemons from "./mainFn/Daemons" export * as health from "./health" export * as healthFns from "./health/checkFns" export * as inits from "./inits" export * as mainFn from "./mainFn" -export * as manifest from "./manifest" export * as toml from "@iarna/toml" -export * as types from "./types" -export * as T from "./types" export * as yaml from "yaml" export * as startSdk from "./StartSdk" -export * as utils from "./util" -export * as matches from "ts-matches" export * as YAML from "yaml" export * as TOML from "@iarna/toml" export * from "./version" diff --git a/sdk/lib/inits/index.ts b/sdk/package/lib/inits/index.ts similarity index 100% rename from sdk/lib/inits/index.ts rename to sdk/package/lib/inits/index.ts diff --git a/sdk/lib/inits/setupInit.ts b/sdk/package/lib/inits/setupInit.ts similarity index 59% rename from sdk/lib/inits/setupInit.ts rename to sdk/package/lib/inits/setupInit.ts index 5fd1c481c..c9ab4f171 100644 --- a/sdk/lib/inits/setupInit.ts +++ b/sdk/package/lib/inits/setupInit.ts @@ -1,9 +1,8 @@ -import { DependenciesReceipt } from "../config/setupConfig" -import { ExtendedVersion, VersionRange } from "../exver" -import { SetInterfaces } from "../interfaces/setupInterfaces" - -import { ExposedStorePaths } from "../store/setupExposeStore" -import * as T from "../types" +import { Actions } from "../../../base/lib/actions/setupActions" +import { ExtendedVersion } from "../../../base/lib/exver" +import { UpdateServiceInterfaces } from "../../../base/lib/interfaces/setupInterfaces" +import { ExposedStorePaths } from "../../../base/lib/types" +import * as T from "../../../base/lib/types" import { VersionGraph } from "../version/VersionGraph" import { Install } from "./setupInstall" import { Uninstall } from "./setupUninstall" @@ -12,18 +11,17 @@ export function setupInit( versions: VersionGraph, install: Install, uninstall: Uninstall, - setInterfaces: SetInterfaces, - setDependencies: (options: { - effects: T.Effects - input: any - }) => Promise, + setServiceInterfaces: UpdateServiceInterfaces, + setDependencies: (options: { effects: T.Effects }) => Promise, + actions: Actions, exposedStore: ExposedStorePaths, ): { - init: T.ExpectedExports.init - uninit: T.ExpectedExports.uninit + packageInit: T.ExpectedExports.packageInit + packageUninit: T.ExpectedExports.packageUninit + containerInit: T.ExpectedExports.containerInit } { return { - init: async (opts) => { + packageInit: async (opts) => { const prev = await opts.effects.getDataVersion() if (prev) { await versions.migrate({ @@ -37,14 +35,8 @@ export function setupInit( version: versions.current.options.version, }) } - await setInterfaces({ - ...opts, - input: null, - }) - await opts.effects.exposeForDependents({ paths: exposedStore }) - await setDependencies({ effects: opts.effects, input: null }) }, - uninit: async (opts) => { + packageUninit: async (opts) => { if (opts.nextVersion) { const prev = await opts.effects.getDataVersion() if (prev) { @@ -58,5 +50,13 @@ export function setupInit( await uninstall.uninstall(opts) } }, + containerInit: async (opts) => { + await setServiceInterfaces({ + ...opts, + }) + await actions.update({ effects: opts.effects }) + await opts.effects.exposeForDependents({ paths: exposedStore }) + await setDependencies({ effects: opts.effects }) + }, } } diff --git a/sdk/lib/inits/setupInstall.ts b/sdk/package/lib/inits/setupInstall.ts similarity index 81% rename from sdk/lib/inits/setupInstall.ts rename to sdk/package/lib/inits/setupInstall.ts index ab21380a0..d9f694021 100644 --- a/sdk/lib/inits/setupInstall.ts +++ b/sdk/package/lib/inits/setupInstall.ts @@ -1,4 +1,4 @@ -import * as T from "../types" +import * as T from "../../../base/lib/types" export type InstallFn = (opts: { effects: T.Effects @@ -11,7 +11,7 @@ export class Install { return new Install(fn) } - async install({ effects }: Parameters[0]) { + async install({ effects }: Parameters[0]) { await this.fn({ effects, }) diff --git a/sdk/lib/inits/setupUninstall.ts b/sdk/package/lib/inits/setupUninstall.ts similarity index 86% rename from sdk/lib/inits/setupUninstall.ts rename to sdk/package/lib/inits/setupUninstall.ts index 918f417e5..c863e1ad7 100644 --- a/sdk/lib/inits/setupUninstall.ts +++ b/sdk/package/lib/inits/setupUninstall.ts @@ -1,4 +1,4 @@ -import * as T from "../types" +import * as T from "../../../base/lib/types" export type UninstallFn = (opts: { effects: T.Effects @@ -14,7 +14,7 @@ export class Uninstall { async uninstall({ effects, nextVersion, - }: Parameters[0]) { + }: Parameters[0]) { if (!nextVersion) await this.fn({ effects, diff --git a/sdk/lib/mainFn/CommandController.ts b/sdk/package/lib/mainFn/CommandController.ts similarity index 94% rename from sdk/lib/mainFn/CommandController.ts rename to sdk/package/lib/mainFn/CommandController.ts index 9c206803a..78d888b95 100644 --- a/sdk/lib/mainFn/CommandController.ts +++ b/sdk/package/lib/mainFn/CommandController.ts @@ -1,15 +1,15 @@ import { DEFAULT_SIGTERM_TIMEOUT } from "." -import { NO_TIMEOUT, SIGKILL, SIGTERM } from "../StartSdk" +import { NO_TIMEOUT, SIGKILL, SIGTERM } from "../../../base/lib/types" -import * as T from "../types" -import { asError } from "../util/asError" +import * as T from "../../../base/lib/types" +import { asError } from "../../../base/lib/util/asError" import { ExecSpawnable, MountOptions, SubContainerHandle, SubContainer, } from "../util/SubContainer" -import { splitCommand } from "../util/splitCommand" +import { splitCommand } from "../util" import * as cp from "child_process" export class CommandController { diff --git a/sdk/lib/mainFn/Daemon.ts b/sdk/package/lib/mainFn/Daemon.ts similarity index 96% rename from sdk/lib/mainFn/Daemon.ts rename to sdk/package/lib/mainFn/Daemon.ts index 6fa1d1085..45f252dac 100644 --- a/sdk/lib/mainFn/Daemon.ts +++ b/sdk/package/lib/mainFn/Daemon.ts @@ -1,5 +1,5 @@ -import * as T from "../types" -import { asError } from "../util/asError" +import * as T from "../../../base/lib/types" +import { asError } from "../../../base/lib/util/asError" import { ExecSpawnable, MountOptions, SubContainer } from "../util/SubContainer" import { CommandController } from "./CommandController" diff --git a/sdk/lib/mainFn/Daemons.ts b/sdk/package/lib/mainFn/Daemons.ts similarity index 87% rename from sdk/lib/mainFn/Daemons.ts rename to sdk/package/lib/mainFn/Daemons.ts index b17f3a1a1..0e05214f7 100644 --- a/sdk/lib/mainFn/Daemons.ts +++ b/sdk/package/lib/mainFn/Daemons.ts @@ -1,19 +1,11 @@ -import { NO_TIMEOUT, SIGKILL, SIGTERM, Signals } from "../StartSdk" -import { HealthReceipt } from "../health/HealthReceipt" +import { HealthReceipt, Signals } from "../../../base/lib/types" + import { HealthCheckResult } from "../health/checkFns" import { Trigger } from "../trigger" -import { TriggerInput } from "../trigger/TriggerInput" -import { defaultTrigger } from "../trigger/defaultTrigger" -import * as T from "../types" +import * as T from "../../../base/lib/types" import { Mounts } from "./Mounts" -import { - CommandOptions, - ExecSpawnable, - MountOptions, - SubContainer, -} from "../util/SubContainer" -import { splitCommand } from "../util/splitCommand" +import { ExecSpawnable, MountOptions } from "../util/SubContainer" import { promisify } from "node:util" import * as CP from "node:child_process" @@ -77,7 +69,9 @@ Daemons.of({ }) ``` */ -export class Daemons { +export class Daemons + implements T.DaemonBuildable +{ private constructor( readonly effects: T.Effects, readonly started: (onTerm: () => PromiseLike) => PromiseLike, @@ -86,23 +80,23 @@ export class Daemons { readonly healthDaemons: HealthDaemon[], ) {} /** - * Returns an empty new Daemons class with the provided config. + * Returns an empty new Daemons class with the provided inputSpec. * * Call .addDaemon() on the returned class to add a daemon. * * Daemons run in the order they are defined, with latter daemons being capable of * depending on prior daemons - * @param config + * @param inputSpec * @returns */ - static of(config: { + static of(inputSpec: { effects: T.Effects started: (onTerm: () => PromiseLike) => PromiseLike healthReceipts: HealthReceipt[] }) { return new Daemons( - config.effects, - config.started, + inputSpec.effects, + inputSpec.started, [], [], [], diff --git a/sdk/lib/mainFn/HealthDaemon.ts b/sdk/package/lib/mainFn/HealthDaemon.ts similarity index 97% rename from sdk/lib/mainFn/HealthDaemon.ts rename to sdk/package/lib/mainFn/HealthDaemon.ts index 7ace3ed7b..71ba83b3f 100644 --- a/sdk/lib/mainFn/HealthDaemon.ts +++ b/sdk/package/lib/mainFn/HealthDaemon.ts @@ -2,9 +2,9 @@ import { HealthCheckResult } from "../health/checkFns" import { defaultTrigger } from "../trigger/defaultTrigger" import { Ready } from "./Daemons" import { Daemon } from "./Daemon" -import { Effects, SetHealth } from "../types" +import { SetHealth, Effects } from "../../../base/lib/types" import { DEFAULT_SIGTERM_TIMEOUT } from "." -import { asError } from "../util/asError" +import { asError } from "../../../base/lib/util/asError" const oncePromise = () => { let resolve: (value: T) => void diff --git a/sdk/lib/mainFn/Mounts.ts b/sdk/package/lib/mainFn/Mounts.ts similarity index 98% rename from sdk/lib/mainFn/Mounts.ts rename to sdk/package/lib/mainFn/Mounts.ts index bd947b759..42d6d66ab 100644 --- a/sdk/lib/mainFn/Mounts.ts +++ b/sdk/package/lib/mainFn/Mounts.ts @@ -1,4 +1,4 @@ -import * as T from "../types" +import * as T from "../../../base/lib/types" import { MountOptions } from "../util/SubContainer" type MountArray = { path: string; options: MountOptions }[] diff --git a/sdk/lib/mainFn/index.ts b/sdk/package/lib/mainFn/index.ts similarity index 77% rename from sdk/lib/mainFn/index.ts rename to sdk/package/lib/mainFn/index.ts index 7a094a31a..ee0481630 100644 --- a/sdk/lib/mainFn/index.ts +++ b/sdk/package/lib/mainFn/index.ts @@ -1,11 +1,7 @@ -import * as T from "../types" +import * as T from "../../../base/lib/types" import { Daemons } from "./Daemons" -import "../interfaces/ServiceInterfaceBuilder" -import "../interfaces/Origin" - -import "./Daemons" - -import { MainEffects } from "../StartSdk" +import "../../../base/lib/interfaces/ServiceInterfaceBuilder" +import "../../../base/lib/interfaces/Origin" export const DEFAULT_SIGTERM_TIMEOUT = 30_000 /** @@ -20,7 +16,7 @@ export const DEFAULT_SIGTERM_TIMEOUT = 30_000 */ export const setupMain = ( fn: (o: { - effects: MainEffects + effects: T.Effects started(onTerm: () => PromiseLike): PromiseLike }) => Promise>, ): T.ExpectedExports.main => { diff --git a/sdk/lib/manifest/setupManifest.ts b/sdk/package/lib/manifest/setupManifest.ts similarity index 75% rename from sdk/lib/manifest/setupManifest.ts rename to sdk/package/lib/manifest/setupManifest.ts index 10aaa03db..088575c1a 100644 --- a/sdk/lib/manifest/setupManifest.ts +++ b/sdk/package/lib/manifest/setupManifest.ts @@ -1,13 +1,17 @@ -import * as T from "../types" -import { ImageConfig, ImageId, VolumeId } from "../osBindings" -import { SDKManifest, SDKImageConfig } from "./ManifestTypes" +import * as T from "../../../base/lib/types" +import { ImageConfig, ImageId, VolumeId } from "../../../base/lib/types" +import { + SDKManifest, + SDKImageInputSpec, +} from "../../../base/lib/types/ManifestTypes" import { SDKVersion } from "../StartSdk" import { VersionGraph } from "../version/VersionGraph" /** - * This is an example of a function that takes a manifest and returns a new manifest with additional properties - * @param manifest Manifests are the description of the package - * @returns The manifest with additional properties + * @description Use this function to define critical information about your package + * + * @param versions Every version of the package, imported from ./versions + * @param manifest Static properties of the package */ export function setupManifest< Id extends string, @@ -20,7 +24,7 @@ export function setupManifest< dependencies: Dependencies id: Id assets: AssetTypes[] - images: Record + images: Record volumes: VolumesTypes[] }, Satisfies extends string[] = [], @@ -56,7 +60,6 @@ export function setupManifest< start: manifest.alerts?.start || null, stop: manifest.alerts?.stop || null, }, - hasConfig: manifest.hasConfig === undefined ? true : manifest.hasConfig, hardwareRequirements: { device: Object.fromEntries( Object.entries(manifest.hardwareRequirements?.device || {}).map( @@ -67,14 +70,14 @@ export function setupManifest< arch: manifest.hardwareRequirements?.arch === undefined ? Object.values(images).reduce( - (arch, config) => { - if (config.emulateMissingAs) { + (arch, inputSpec) => { + if (inputSpec.emulateMissingAs) { return arch } if (arch === null) { - return config.arch + return inputSpec.arch } - return arch.filter((a) => config.arch.includes(a)) + return arch.filter((a) => inputSpec.arch.includes(a)) }, null as string[] | null, ) diff --git a/sdk/lib/store/getStore.ts b/sdk/package/lib/store/getStore.ts similarity index 90% rename from sdk/lib/store/getStore.ts rename to sdk/package/lib/store/getStore.ts index 5250a02a1..3bde46a25 100644 --- a/sdk/lib/store/getStore.ts +++ b/sdk/package/lib/store/getStore.ts @@ -1,5 +1,5 @@ -import { Effects } from "../types" -import { PathBuilder, extractJsonPath } from "./PathBuilder" +import { Effects } from "../../../base/lib/Effects" +import { PathBuilder, extractJsonPath } from "../util" export class GetStore { constructor( @@ -18,7 +18,7 @@ export class GetStore { return this.effects.store.get({ ...this.options, path: extractJsonPath(this.path), - callback: this.effects.restart, + callback: () => this.effects.constRetry(), }) } /** diff --git a/sdk/package/lib/store/setupExposeStore.ts b/sdk/package/lib/store/setupExposeStore.ts new file mode 100644 index 000000000..1ae0bf13f --- /dev/null +++ b/sdk/package/lib/store/setupExposeStore.ts @@ -0,0 +1,28 @@ +import { ExposedStorePaths } from "../../../base/lib/types" +import { Affine, _ } from "../util" +import { + PathBuilder, + extractJsonPath, + pathBuilder, +} from "../../../base/lib/util/PathBuilder" + +/** + * @description Use this function to determine which Store values to expose and make available to other services running on StartOS. Store values not exposed here will be kept private. Use the type safe pathBuilder to traverse your Store's structure. + * @example + * In this example, we expose the hypothetical Store values "adminPassword" and "nameLastUpdatedAt". + * + * ``` + export const exposedStore = setupExposeStore((pathBuilder) => [ + pathBuilder.adminPassword + pathBuilder.nameLastUpdatedAt, + ]) + * ``` + */ +export const setupExposeStore = >( + fn: (pathBuilder: PathBuilder) => PathBuilder[], +) => { + return fn(pathBuilder()).map( + (x) => extractJsonPath(x) as string, + ) as ExposedStorePaths +} +export { ExposedStorePaths } diff --git a/sdk/lib/test/health.readyCheck.test.ts b/sdk/package/lib/test/health.readyCheck.test.ts similarity index 100% rename from sdk/lib/test/health.readyCheck.test.ts rename to sdk/package/lib/test/health.readyCheck.test.ts diff --git a/sdk/lib/test/host.test.ts b/sdk/package/lib/test/host.test.ts similarity index 78% rename from sdk/lib/test/host.test.ts rename to sdk/package/lib/test/host.test.ts index 64d486a94..87f22b8bd 100644 --- a/sdk/lib/test/host.test.ts +++ b/sdk/package/lib/test/host.test.ts @@ -1,6 +1,6 @@ -import { ServiceInterfaceBuilder } from "../interfaces/ServiceInterfaceBuilder" -import { Effects } from "../types" -import { sdk } from "./output.sdk" +import { ServiceInterfaceBuilder } from "../../../base/lib/interfaces/ServiceInterfaceBuilder" +import { Effects } from "../../../base/lib/Effects" +import { sdk } from "../test/output.sdk" describe("host", () => { test("Testing that the types work", () => { diff --git a/sdk/lib/test/configBuilder.test.ts b/sdk/package/lib/test/inputSpecBuilder.test.ts similarity index 94% rename from sdk/lib/test/configBuilder.test.ts rename to sdk/package/lib/test/inputSpecBuilder.test.ts index 5c65271a8..bb5a8e25d 100644 --- a/sdk/lib/test/configBuilder.test.ts +++ b/sdk/package/lib/test/inputSpecBuilder.test.ts @@ -1,9 +1,9 @@ import { testOutput } from "./output.test" -import { Config } from "../config/builder/config" -import { List } from "../config/builder/list" -import { Value } from "../config/builder/value" -import { Variants } from "../config/builder/variants" -import { ValueSpec } from "../config/configTypes" +import { InputSpec } from "../../../base/lib/actions/input/builder/inputSpec" +import { List } from "../../../base/lib/actions/input/builder/list" +import { Value } from "../../../base/lib/actions/input/builder/value" +import { Variants } from "../../../base/lib/actions/input/builder/variants" +import { ValueSpec } from "../../../base/lib/actions/input/inputSpecTypes" import { setupManifest } from "../manifest/setupManifest" import { StartSdk } from "../StartSdk" import { VersionGraph } from "../version/VersionGraph" @@ -13,7 +13,7 @@ describe("builder tests", () => { test("text", async () => { const bitcoinPropertiesBuilt: { "peer-tor-address": ValueSpec - } = await Config.of({ + } = await InputSpec.of({ "peer-tor-address": Value.text({ name: "Peer tor address", description: "The Tor address of the peer interface", @@ -232,9 +232,8 @@ describe("values", () => { { name: "Testing", description: null, - warning: null, }, - Config.of({ + InputSpec.of({ a: Value.toggle({ name: "test", description: null, @@ -258,7 +257,7 @@ describe("values", () => { Variants.of({ a: { name: "a", - spec: Config.of({ + spec: InputSpec.of({ b: Value.toggle({ name: "b", description: null, @@ -277,7 +276,7 @@ describe("values", () => { describe("dynamic", () => { const fakeOptions = { - config: "config", + inputSpec: "inputSpec", effects: "effects", utils: "utils", } as any @@ -546,7 +545,7 @@ describe("values", () => { Variants.of({ a: { name: "a", - spec: Config.of({ + spec: InputSpec.of({ b: Value.toggle({ name: "b", description: null, @@ -557,7 +556,7 @@ describe("values", () => { }, b: { name: "b", - spec: Config.of({ + spec: InputSpec.of({ b: Value.toggle({ name: "b", description: null, @@ -613,7 +612,7 @@ describe("values", () => { Variants.of({ a: { name: "a", - spec: Config.of({ + spec: InputSpec.of({ b: Value.toggle({ name: "b", description: null, @@ -624,7 +623,7 @@ describe("values", () => { }, b: { name: "b", - spec: Config.of({ + spec: InputSpec.of({ b: Value.toggle({ name: "b", description: null, @@ -679,7 +678,7 @@ describe("Builder List", () => { name: "test", }, { - spec: Config.of({ + spec: InputSpec.of({ test: Value.toggle({ name: "test", description: null, @@ -732,11 +731,11 @@ describe("Builder List", () => { describe("Nested nullable values", () => { test("Testing text", async () => { - const value = Config.of({ + const value = InputSpec.of({ a: Value.text({ name: "Temp Name", description: - "If no name is provided, the name from config will be used", + "If no name is provided, the name from inputSpec will be used", required: false, }), }) @@ -747,11 +746,11 @@ describe("Nested nullable values", () => { testOutput()(null) }) test("Testing number", async () => { - const value = Config.of({ + const value = InputSpec.of({ a: Value.number({ name: "Temp Name", description: - "If no name is provided, the name from config will be used", + "If no name is provided, the name from inputSpec will be used", required: false, warning: null, placeholder: null, @@ -769,11 +768,11 @@ describe("Nested nullable values", () => { testOutput()(null) }) test("Testing color", async () => { - const value = Config.of({ + const value = InputSpec.of({ a: Value.color({ name: "Temp Name", description: - "If no name is provided, the name from config will be used", + "If no name is provided, the name from inputSpec will be used", required: false, warning: null, }), @@ -785,11 +784,11 @@ describe("Nested nullable values", () => { testOutput()(null) }) test("Testing select", async () => { - const value = Config.of({ + const value = InputSpec.of({ a: Value.select({ name: "Temp Name", description: - "If no name is provided, the name from config will be used", + "If no name is provided, the name from inputSpec will be used", required: false, warning: null, values: { @@ -799,7 +798,8 @@ describe("Nested nullable values", () => { }) const higher = await Value.select({ name: "Temp Name", - description: "If no name is provided, the name from config will be used", + description: + "If no name is provided, the name from inputSpec will be used", required: false, warning: null, values: { @@ -814,11 +814,11 @@ describe("Nested nullable values", () => { testOutput()(null) }) test("Testing multiselect", async () => { - const value = Config.of({ + const value = InputSpec.of({ a: Value.multiselect({ name: "Temp Name", description: - "If no name is provided, the name from config will be used", + "If no name is provided, the name from inputSpec will be used", warning: null, default: [], diff --git a/sdk/lib/test/makeOutput.ts b/sdk/package/lib/test/makeOutput.ts similarity index 99% rename from sdk/lib/test/makeOutput.ts rename to sdk/package/lib/test/makeOutput.ts index cef17a7e8..434484be9 100644 --- a/sdk/lib/test/makeOutput.ts +++ b/sdk/package/lib/test/makeOutput.ts @@ -3,7 +3,7 @@ import { oldSpecToBuilder } from "../../scripts/oldSpecToBuilder" oldSpecToBuilder( // Make the location "./lib/test/output.ts", - // Put the config here + // Put the inputSpec here { mediasources: { type: "list", diff --git a/sdk/lib/test/output.sdk.ts b/sdk/package/lib/test/output.sdk.ts similarity index 100% rename from sdk/lib/test/output.sdk.ts rename to sdk/package/lib/test/output.sdk.ts diff --git a/sdk/lib/test/output.test.ts b/sdk/package/lib/test/output.test.ts similarity index 72% rename from sdk/lib/test/output.test.ts rename to sdk/package/lib/test/output.test.ts index 1df84f3af..37636e52f 100644 --- a/sdk/lib/test/output.test.ts +++ b/sdk/package/lib/test/output.test.ts @@ -1,7 +1,7 @@ -import { ConfigSpec, matchConfigSpec } from "./output" +import { InputSpecSpec, matchInputSpecSpec } from "./output" import * as _I from "../index" import { camelCase } from "../../scripts/oldSpecToBuilder" -import { deepMerge } from "../util/deepMerge" +import { deepMerge } from "../../../base/lib/util" export type IfEquals = (() => G extends T ? 1 : 2) extends () => G extends U ? 1 : 2 ? Y : N @@ -10,37 +10,40 @@ export function testOutput(): (c: IfEquals) => null { } /// Testing the types of the input spec -testOutput()(null) -testOutput()(null) -testOutput()(null) +testOutput()(null) +testOutput()(null) +testOutput()(null) -testOutput()(null) +testOutput()(null) testOutput< - ConfigSpec["rpc"]["advanced"]["serialversion"], + InputSpecSpec["rpc"]["advanced"]["serialversion"], "segwit" | "non-segwit" >()(null) -testOutput()(null) +testOutput()(null) testOutput< - ConfigSpec["advanced"]["peers"]["addnode"][0]["hostname"], + InputSpecSpec["advanced"]["peers"]["addnode"][0]["hostname"], string | null | undefined >()(null) -testOutput()( +testOutput< + InputSpecSpec["testListUnion"][0]["union"]["value"]["name"], + string +>()(null) +testOutput()( null, ) -testOutput()(null) -testOutput>()( +testOutput>()( null, ) // @ts-expect-error Because enable should be a boolean -testOutput()(null) +testOutput()(null) // prettier-ignore // @ts-expect-error Expect that the string is the one above -testOutput()(null); +testOutput()(null); -/// Here we test the output of the matchConfigSpec function +/// Here we test the output of the matchInputSpecSpec function describe("Inputs", () => { - const validInput: ConfigSpec = { + const validInput: InputSpecSpec = { mediasources: ["filebrowser"], testListUnion: [ { @@ -95,24 +98,24 @@ describe("Inputs", () => { } test("test valid input", () => { - const output = matchConfigSpec.unsafeCast(validInput) + const output = matchInputSpecSpec.unsafeCast(validInput) expect(output).toEqual(validInput) }) test("test no longer care about the conversion of min/max and validating", () => { - matchConfigSpec.unsafeCast( + matchInputSpecSpec.unsafeCast( deepMerge({}, validInput, { rpc: { advanced: { threads: 0 } } }), ) }) test("test errors should throw for number in string", () => { expect(() => - matchConfigSpec.unsafeCast( + matchInputSpecSpec.unsafeCast( deepMerge({}, validInput, { rpc: { enable: 2 } }), ), ).toThrowError() }) test("Test that we set serialversion to something not segwit or non-segwit", () => { expect(() => - matchConfigSpec.unsafeCast( + matchInputSpecSpec.unsafeCast( deepMerge({}, validInput, { rpc: { advanced: { serialversion: "testing" } }, }), diff --git a/sdk/lib/test/store.test.ts b/sdk/package/lib/test/store.test.ts similarity index 52% rename from sdk/lib/test/store.test.ts rename to sdk/package/lib/test/store.test.ts index fa7bc4a4c..f876e76d8 100644 --- a/sdk/lib/test/store.test.ts +++ b/sdk/package/lib/test/store.test.ts @@ -1,9 +1,9 @@ -import { MainEffects, StartSdk } from "../StartSdk" -import { extractJsonPath } from "../store/PathBuilder" -import { Effects } from "../types" +import { Effects } from "../../../base/lib/types" +import { extractJsonPath } from "../../../base/lib/util/PathBuilder" +import { StartSdk } from "../StartSdk" type Store = { - config: { + inputSpec: { someValue: "a" | "b" } } @@ -23,16 +23,16 @@ const storePath = sdk.StorePath describe("Store", () => { test("types", async () => { ;async () => { - sdk.store.setOwn(todo(), storePath.config, { + sdk.store.setOwn(todo(), storePath.inputSpec, { someValue: "a", }) - sdk.store.setOwn(todo(), storePath.config.someValue, "b") + sdk.store.setOwn(todo(), storePath.inputSpec.someValue, "b") sdk.store.setOwn(todo(), storePath, { - config: { someValue: "b" }, + inputSpec: { someValue: "b" }, }) sdk.store.setOwn( todo(), - storePath.config.someValue, + storePath.inputSpec.someValue, // @ts-expect-error Type is wrong for the setting value 5, @@ -40,73 +40,70 @@ describe("Store", () => { sdk.store.setOwn( todo(), // @ts-expect-error Path is wrong - "/config/someVae3lue", + "/inputSpec/someVae3lue", "someValue", ) todo().store.set({ - path: extractJsonPath(storePath.config.someValue), + path: extractJsonPath(storePath.inputSpec.someValue), value: "b", }) - todo().store.set({ - path: extractJsonPath(storePath.config.someValue), + todo().store.set({ + path: extractJsonPath(storePath.inputSpec.someValue), //@ts-expect-error Path is wrong value: "someValueIn", }) ;(await sdk.store - .getOwn(todo(), storePath.config.someValue) + .getOwn(todo(), storePath.inputSpec.someValue) .const()) satisfies string ;(await sdk.store - .getOwn(todo(), storePath.config) - .const()) satisfies Store["config"] + .getOwn(todo(), storePath.inputSpec) + .const()) satisfies Store["inputSpec"] await sdk.store // @ts-expect-error Path is wrong - .getOwn(todo(), "/config/somdsfeValue") + .getOwn(todo(), "/inputSpec/somdsfeValue") .const() /// ----------------- ERRORS ----------------- - sdk.store.setOwn(todo(), storePath, { + sdk.store.setOwn(todo(), storePath, { // @ts-expect-error Type is wrong for the setting value - config: { someValue: "notInAOrB" }, + inputSpec: { someValue: "notInAOrB" }, }) sdk.store.setOwn( - todo(), - sdk.StorePath.config.someValue, + todo(), + sdk.StorePath.inputSpec.someValue, // @ts-expect-error Type is wrong for the setting value "notInAOrB", ) ;(await sdk.store - .getOwn(todo(), storePath.config.someValue) - // @ts-expect-error Const should normally not be callable + .getOwn(todo(), storePath.inputSpec.someValue) .const()) satisfies string ;(await sdk.store - .getOwn(todo(), storePath.config) - // @ts-expect-error Const should normally not be callable - .const()) satisfies Store["config"] + .getOwn(todo(), storePath.inputSpec) + .const()) satisfies Store["inputSpec"] await sdk.store // @ts-expect-error Path is wrong - .getOwn("/config/somdsfeValue") - // @ts-expect-error Const should normally not be callable + .getOwn("/inputSpec/somdsfeValue") .const() /// ;(await sdk.store - .getOwn(todo(), storePath.config.someValue) + .getOwn(todo(), storePath.inputSpec.someValue) // @ts-expect-error satisfies type is wrong .const()) satisfies number await sdk.store // @ts-expect-error Path is wrong - .getOwn(todo(), extractJsonPath(storePath.config)) + .getOwn(todo(), extractJsonPath(storePath.inputSpec)) .const() ;(await todo().store.get({ - path: extractJsonPath(storePath.config.someValue), + path: extractJsonPath(storePath.inputSpec.someValue), callback: noop, })) satisfies string - await todo().store.get({ + await todo().store.get({ // @ts-expect-error Path is wrong as in it doesn't match above - path: "/config/someV2alue", + path: "/inputSpec/someV2alue", callback: noop, }) - await todo().store.get({ + await todo().store.get({ // @ts-expect-error Path is wrong as in it doesn't exists in wrapper type - path: "/config/someV2alue", + path: "/inputSpec/someV2alue", callback: noop, }) } diff --git a/sdk/lib/trigger/TriggerInput.ts b/sdk/package/lib/trigger/TriggerInput.ts similarity index 52% rename from sdk/lib/trigger/TriggerInput.ts rename to sdk/package/lib/trigger/TriggerInput.ts index 82fe79e07..e15cca9b7 100644 --- a/sdk/lib/trigger/TriggerInput.ts +++ b/sdk/package/lib/trigger/TriggerInput.ts @@ -1,4 +1,4 @@ -import { HealthStatus } from "../types" +import { HealthStatus } from "../../../base/lib/types" export type TriggerInput = { lastResult?: HealthStatus diff --git a/sdk/lib/trigger/changeOnFirstSuccess.ts b/sdk/package/lib/trigger/changeOnFirstSuccess.ts similarity index 100% rename from sdk/lib/trigger/changeOnFirstSuccess.ts rename to sdk/package/lib/trigger/changeOnFirstSuccess.ts diff --git a/sdk/lib/trigger/cooldownTrigger.ts b/sdk/package/lib/trigger/cooldownTrigger.ts similarity index 100% rename from sdk/lib/trigger/cooldownTrigger.ts rename to sdk/package/lib/trigger/cooldownTrigger.ts diff --git a/sdk/lib/trigger/defaultTrigger.ts b/sdk/package/lib/trigger/defaultTrigger.ts similarity index 100% rename from sdk/lib/trigger/defaultTrigger.ts rename to sdk/package/lib/trigger/defaultTrigger.ts diff --git a/sdk/lib/trigger/index.ts b/sdk/package/lib/trigger/index.ts similarity index 83% rename from sdk/lib/trigger/index.ts rename to sdk/package/lib/trigger/index.ts index eb058437f..6da034262 100644 --- a/sdk/lib/trigger/index.ts +++ b/sdk/package/lib/trigger/index.ts @@ -1,4 +1,3 @@ -import { ExecSpawnable } from "../util/SubContainer" import { TriggerInput } from "./TriggerInput" export { changeOnFirstSuccess } from "./changeOnFirstSuccess" export { cooldownTrigger } from "./cooldownTrigger" diff --git a/sdk/lib/trigger/lastStatus.ts b/sdk/package/lib/trigger/lastStatus.ts similarity index 93% rename from sdk/lib/trigger/lastStatus.ts rename to sdk/package/lib/trigger/lastStatus.ts index 90b8c9851..01e737314 100644 --- a/sdk/lib/trigger/lastStatus.ts +++ b/sdk/package/lib/trigger/lastStatus.ts @@ -1,5 +1,5 @@ import { Trigger } from "." -import { HealthStatus } from "../types" +import { HealthStatus } from "../../../base/lib/types" export type LastStatusTriggerParams = { [k in HealthStatus]?: Trigger } & { default: Trigger diff --git a/sdk/lib/trigger/successFailure.ts b/sdk/package/lib/trigger/successFailure.ts similarity index 100% rename from sdk/lib/trigger/successFailure.ts rename to sdk/package/lib/trigger/successFailure.ts diff --git a/sdk/lib/util/GetSslCertificate.ts b/sdk/package/lib/util/GetSslCertificate.ts similarity index 91% rename from sdk/lib/util/GetSslCertificate.ts rename to sdk/package/lib/util/GetSslCertificate.ts index df19607d4..72bf9e22d 100644 --- a/sdk/lib/util/GetSslCertificate.ts +++ b/sdk/package/lib/util/GetSslCertificate.ts @@ -1,5 +1,5 @@ import { T } from ".." -import { Effects } from "../types" +import { Effects } from "../../../base/lib/Effects" export class GetSslCertificate { constructor( @@ -15,7 +15,7 @@ export class GetSslCertificate { return this.effects.getSslCertificate({ hostnames: this.hostnames, algorithm: this.algorithm, - callback: this.effects.restart, + callback: () => this.effects.constRetry(), }) } /** diff --git a/sdk/lib/util/SubContainer.ts b/sdk/package/lib/util/SubContainer.ts similarity index 98% rename from sdk/lib/util/SubContainer.ts rename to sdk/package/lib/util/SubContainer.ts index 82c1ae1b3..b6a7492a7 100644 --- a/sdk/lib/util/SubContainer.ts +++ b/sdk/package/lib/util/SubContainer.ts @@ -1,9 +1,9 @@ import * as fs from "fs/promises" -import * as T from "../types" +import * as T from "../../../base/lib/types" import * as cp from "child_process" import { promisify } from "util" import { Buffer } from "node:buffer" -import { once } from "./once" +import { once } from "../../../base/lib/util/once" export const execFile = promisify(cp.execFile) const WORKDIR = (imageId: string) => `/media/startos/images/${imageId}/` const False = () => false @@ -266,8 +266,8 @@ export class SubContainer implements ExecSpawnable { appendTo.data += chunk } else if (typeof chunk === "string" || chunk instanceof Buffer) { appendTo.data = Buffer.concat([ - Buffer.from(appendTo.data), - Buffer.from(chunk), + new Uint8Array(Buffer.from(appendTo.data).buffer), + new Uint8Array(Buffer.from(chunk).buffer), ]) } else { console.error("received unexpected chunk", chunk) diff --git a/sdk/package/lib/util/fileHelper.ts b/sdk/package/lib/util/fileHelper.ts new file mode 100644 index 000000000..bb7190fe1 --- /dev/null +++ b/sdk/package/lib/util/fileHelper.ts @@ -0,0 +1,214 @@ +import * as matches from "ts-matches" +import * as YAML from "yaml" +import * as TOML from "@iarna/toml" +import merge from "lodash.merge" +import * as T from "../../../base/lib/types" +import * as fs from "node:fs/promises" +import { asError } from "../../../base/lib/util" + +const previousPath = /(.+?)\/([^/]*)$/ + +const exists = (path: string) => + fs.access(path).then( + () => true, + () => false, + ) + +async function onCreated(path: string) { + if (path === "/") return + if (!path.startsWith("/")) path = `${process.cwd()}/${path}` + if (await exists(path)) { + return + } + const split = path.split("/") + const filename = split.pop() + const parent = split.join("/") + await onCreated(parent) + const ctrl = new AbortController() + const watch = fs.watch(parent, { persistent: false, signal: ctrl.signal }) + if ( + await fs.access(path).then( + () => true, + () => false, + ) + ) { + ctrl.abort("finished") + return + } + for await (let event of watch) { + if (event.filename === filename) { + ctrl.abort("finished") + return + } + } +} + +/** + * @description Use this class to read/write an underlying configuration file belonging to the upstream service. + * + * Using the static functions, choose between officially supported file formats (json, yaml, toml), or a custom format (raw). + * @example + * Below are a few examples + * + * ``` + * import { matches, FileHelper } from '@start9labs/start-sdk' + * const { arrayOf, boolean, literal, literals, object, oneOf, natural, string } = matches + * + * export const jsonFile = FileHelper.json('./inputSpec.json', object({ + * passwords: arrayOf(string) + * type: oneOf(literals('private', 'public')) + * })) + * + * export const tomlFile = FileHelper.toml('./inputSpec.toml', object({ + * url: literal('https://start9.com') + * public: boolean + * })) + * + * export const yamlFile = FileHelper.yaml('./inputSpec.yml', object({ + * name: string + * age: natural + * })) + * + * export const bitcoinConfFile = FileHelper.raw( + * './service.conf', + * (obj: CustomType) => customConvertObjToFormattedString(obj), + * (str) => customParseStringToTypedObj(str), + * ) + * ``` + */ +export class FileHelper { + protected constructor( + readonly path: string, + readonly writeData: (dataIn: A) => string, + readonly readData: (stringValue: string) => A, + ) {} + + /** + * Accepts structured data and overwrites the existing file on disk. + */ + async write(data: A) { + const parent = previousPath.exec(this.path) + if (parent) { + await fs.mkdir(parent[1], { recursive: true }) + } + + await fs.writeFile(this.path, this.writeData(data)) + } + + /** + * Reads the file from disk and converts it to structured data. + */ + async read() { + if (!(await exists(this.path))) { + return null + } + return this.readData( + await fs.readFile(this.path).then((data) => data.toString("utf-8")), + ) + } + + async const(effects: T.Effects) { + const watch = this.watch() + const res = await watch.next() + watch.next().then(effects.constRetry) + return res.value + } + + async *watch() { + let res + while (true) { + if (await exists(this.path)) { + const ctrl = new AbortController() + const watch = fs.watch(this.path, { + persistent: false, + signal: ctrl.signal, + }) + res = await this.read() + const listen = Promise.resolve() + .then(async () => { + for await (const _ of watch) { + ctrl.abort("finished") + return + } + }) + .catch((e) => console.error(asError(e))) + yield res + await listen + } else { + yield null + await onCreated(this.path).catch((e) => console.error(asError(e))) + } + } + } + + /** + * Accepts structured data and performs a merge with the existing file on disk. + */ + async merge(data: A) { + const fileData = (await this.read().catch(() => ({}))) || {} + const mergeData = merge({}, fileData, data) + return await this.write(mergeData) + } + /** + * Create a File Helper for an arbitrary file type. + * + * Provide custom functions for translating data to/from the file format. + */ + static raw( + path: string, + toFile: (dataIn: A) => string, + fromFile: (rawData: string) => A, + ) { + return new FileHelper(path, toFile, fromFile) + } + /** + * Create a File Helper for a .json file. + */ + static json(path: string, shape: matches.Validator) { + return new FileHelper( + path, + (inData) => { + return JSON.stringify(inData, null, 2) + }, + (inString) => { + return shape.unsafeCast(JSON.parse(inString)) + }, + ) + } + /** + * Create a File Helper for a .toml file + */ + static toml>( + path: string, + shape: matches.Validator, + ) { + return new FileHelper( + path, + (inData) => { + return TOML.stringify(inData as any) + }, + (inString) => { + return shape.unsafeCast(TOML.parse(inString)) + }, + ) + } + /** + * Create a File Helper for a .yaml file + */ + static yaml>( + path: string, + shape: matches.Validator, + ) { + return new FileHelper( + path, + (inData) => { + return YAML.stringify(inData, null, 2) + }, + (inString) => { + return shape.unsafeCast(YAML.parse(inString)) + }, + ) + } +} + +export default FileHelper diff --git a/sdk/package/lib/util/index.ts b/sdk/package/lib/util/index.ts new file mode 100644 index 000000000..66c73503e --- /dev/null +++ b/sdk/package/lib/util/index.ts @@ -0,0 +1,4 @@ +export * from "../../../base/lib/util" +export { GetSslCertificate } from "./GetSslCertificate" + +export { hostnameInfoToAddress } from "../../../base/lib/util/Hostname" diff --git a/sdk/lib/version/VersionGraph.ts b/sdk/package/lib/version/VersionGraph.ts similarity index 94% rename from sdk/lib/version/VersionGraph.ts rename to sdk/package/lib/version/VersionGraph.ts index 1b6b49bb9..91c7a0cc0 100644 --- a/sdk/lib/version/VersionGraph.ts +++ b/sdk/package/lib/version/VersionGraph.ts @@ -1,8 +1,6 @@ -import { ExtendedVersion, VersionRange } from "../exver" - -import * as T from "../types" -import { Graph, Vertex } from "../util/graph" -import { once } from "../util/once" +import { ExtendedVersion, VersionRange } from "../../../base/lib/exver" +import * as T from "../../../base/lib/types" +import { Graph, Vertex, once } from "../util" import { IMPOSSIBLE, VersionInfo } from "./VersionInfo" export class VersionGraph { @@ -110,6 +108,11 @@ export class VersionGraph { currentVersion = once(() => ExtendedVersion.parse(this.current.options.version), ) + /** + * Each exported `VersionInfo.of()` should be imported and provided as an argument to this function. + * + * ** The current version must be the FIRST argument. ** + */ static of< CurrentVersion extends string, OtherVersions extends Array>, diff --git a/sdk/lib/version/VersionInfo.ts b/sdk/package/lib/version/VersionInfo.ts similarity index 68% rename from sdk/lib/version/VersionInfo.ts rename to sdk/package/lib/version/VersionInfo.ts index beea16019..952ae5352 100644 --- a/sdk/lib/version/VersionInfo.ts +++ b/sdk/package/lib/version/VersionInfo.ts @@ -1,29 +1,27 @@ -import { ValidateExVer } from "../exver" -import * as T from "../types" +import { ValidateExVer } from "../../../base/lib/exver" +import * as T from "../../../base/lib/types" export const IMPOSSIBLE = Symbol("IMPOSSIBLE") export type VersionOptions = { - /** The version being described */ + /** The exver-compliant version number */ version: Version & ValidateExVer /** The release notes for this version */ releaseNotes: string /** Data migrations for this version */ migrations: { /** - * A migration from the previous version - * Leave blank to indicate no migration is necessary - * Set to `IMPOSSIBLE` to indicate migrating from the previous version is not possible + * A migration from the previous version. Leave empty to indicate no migration is necessary. + * Set to `IMPOSSIBLE` to indicate migrating from the previous version is not possible. */ up?: ((opts: { effects: T.Effects }) => Promise) | typeof IMPOSSIBLE /** - * A migration to the previous version - * Leave blank to indicate no migration is necessary - * Set to `IMPOSSIBLE` to indicate downgrades are prohibited + * A migration to the previous version. Leave blank to indicate no migration is necessary. + * Set to `IMPOSSIBLE` to indicate downgrades are prohibited */ down?: ((opts: { effects: T.Effects }) => Promise) | typeof IMPOSSIBLE /** - * Additional migrations, such as fast-forward migrations, or migrations from other flavors + * Additional migrations, such as fast-forward migrations, or migrations from other flavors. */ other?: Record Promise> } @@ -34,6 +32,13 @@ export class VersionInfo { private constructor( readonly options: VersionOptions & { satisfies: string[] }, ) {} + /** + * @description Use this function to define a new version of the service. By convention, each version should receive its own file. + * @property {string} version + * @property {string} releaseNotes + * @property {object} migrations + * @returns A VersionInfo class instance that is exported, then imported into versions/index.ts. + */ static of(options: VersionOptions) { return new VersionInfo({ ...options, satisfies: [] }) } diff --git a/sdk/lib/version/index.ts b/sdk/package/lib/version/index.ts similarity index 100% rename from sdk/lib/version/index.ts rename to sdk/package/lib/version/index.ts diff --git a/sdk/package/package-lock.json b/sdk/package/package-lock.json new file mode 100644 index 000000000..b5c0fba5d --- /dev/null +++ b/sdk/package/package-lock.json @@ -0,0 +1,4706 @@ +{ + "name": "@start9labs/start-sdk", + "version": "0.3.6-alpha8", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@start9labs/start-sdk", + "version": "0.3.6-alpha8", + "license": "MIT", + "dependencies": { + "@iarna/toml": "^2.2.5", + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0", + "isomorphic-fetch": "^3.0.0", + "lodash.merge": "^4.6.2", + "mime": "^4.0.3", + "ts-matches": "^5.5.1", + "yaml": "^2.2.2" + }, + "devDependencies": { + "@types/jest": "^29.4.0", + "@types/lodash.merge": "^4.6.2", + "jest": "^29.4.3", + "peggy": "^3.0.2", + "prettier": "^3.2.5", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.1", + "ts-pegjs": "^4.2.1", + "tsx": "^4.7.1", + "typescript": "^5.0.4" + } + }, + "../base": { + "name": "@start9labs/start-sdk-base", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@iarna/toml": "^2.2.5", + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0", + "isomorphic-fetch": "^3.0.0", + "lodash.merge": "^4.6.2", + "mime": "^4.0.3", + "ts-matches": "^5.5.1", + "yaml": "^2.2.2" + }, + "devDependencies": { + "@types/jest": "^29.4.0", + "@types/lodash.merge": "^4.6.2", + "jest": "^29.4.3", + "peggy": "^3.0.2", + "prettier": "^3.2.5", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.1", + "ts-pegjs": "^4.2.1", + "tsx": "^4.7.1", + "typescript": "^5.0.4" + } + }, + "../base/dist": { + "extraneous": true + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", + "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.3.tgz", + "integrity": "sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.21.3", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.21.2", + "@babel/helpers": "^7.21.0", + "@babel/parser": "^7.21.3", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.3", + "@babel/types": "^7.21.3", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/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/@babel/generator": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.3.tgz", + "integrity": "sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.21.3", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "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/@babel/helper-compilation-targets": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", + "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.20.5", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", + "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.2", + "@babel/types": "^7.21.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", + "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/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/@babel/highlight/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/@babel/highlight/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/@babel/highlight/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/@babel/highlight/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/@babel/highlight/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/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz", + "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.3.tgz", + "integrity": "sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.21.3", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.21.3", + "@babel/types": "^7.21.3", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz", + "integrity": "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@iarna/toml": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", + "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" + }, + "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==", + "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" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.5.0.tgz", + "integrity": "sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.5.0.tgz", + "integrity": "sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.5.0", + "@jest/reporters": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@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.5.0", + "jest-config": "^29.5.0", + "jest-haste-map": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-resolve-dependencies": "^29.5.0", + "jest-runner": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "jest-watcher": "^29.5.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "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/environment": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz", + "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-mock": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==", + "dev": true, + "dependencies": { + "expect": "^29.5.0", + "jest-snapshot": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", + "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.4.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz", + "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.5.0", + "jest-mock": "^29.5.0", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.5.0.tgz", + "integrity": "sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.5.0", + "@jest/expect": "^29.5.0", + "@jest/types": "^29.5.0", + "jest-mock": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.5.0.tgz", + "integrity": "sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@jridgewell/trace-mapping": "^0.3.15", + "@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": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "jest-worker": "^29.5.0", + "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" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", + "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.25.16" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.3.tgz", + "integrity": "sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.15", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.5.0.tgz", + "integrity": "sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz", + "integrity": "sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.5.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.5.0.tgz", + "integrity": "sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.5.0", + "@jridgewell/trace-mapping": "^0.3.15", + "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.5.0", + "jest-regex-util": "^29.4.3", + "jest-util": "^29.5.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", + "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.4.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/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "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.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@noble/curves": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.0.tgz", + "integrity": "sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "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": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.25.24", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", + "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0" + } + }, + "node_modules/@ts-morph/common": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.19.0.tgz", + "integrity": "sha512-Unz/WHmd4pGax91rdIKWi51wnVUW11QttMEPpBiBgIewnc9UQIX7UDLxr5vRlqeByXCwhkF6VabSsI0raWcyAQ==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.12", + "minimatch": "^7.4.3", + "mkdirp": "^2.1.6", + "path-browserify": "^1.0.1" + } + }, + "node_modules/@ts-morph/common/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@ts-morph/common/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==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", + "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", + "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.18.3", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", + "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "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.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.0.tgz", + "integrity": "sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/lodash": { + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.5.tgz", + "integrity": "sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==", + "dev": true + }, + "node_modules/@types/lodash.merge": { + "version": "4.6.9", + "resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.9.tgz", + "integrity": "sha512-23sHDPmzd59kUgWyKGiOMO2Qb9YtqRO/x4IhkgNUiPQ1+5MUVqi6bCZeq9nBJ17msjIMbEIO5u+XW4Kz6aGUhQ==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/node": { + "version": "18.15.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.10.tgz", + "integrity": "sha512-9avDaQJczATcXgfmMAW3MIWArOO7A+m90vuCFLr8AotWf8igO/mRoYukrk2cqZVtv38tHs33retzHEilM7FpeQ==", + "dev": true + }, + "node_modules/@types/prettier": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", + "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", + "dev": true + }, + "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/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/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "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/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-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/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/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "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/babel-jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz", + "integrity": "sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.5.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.5.0", + "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-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.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", + "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", + "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.5.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", + "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.5.0", + "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==", + "dev": true + }, + "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/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.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" + }, + "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/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.30001470", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001470.tgz", + "integrity": "sha512-065uNwY6QtHCBOExzbV6m236DDhYCCtPmQUCoQtwkVqzud8v5QPidoMr6CoMkC2nfp6nksjttqWQRRh75LqUmA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "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/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.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true + }, + "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/cliui/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/cliui/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/cliui/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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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/code-block-writer": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz", + "integrity": "sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==", + "dev": true + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "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/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/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "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": "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/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "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": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "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/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": 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/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.4.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", + "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.341", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.341.tgz", + "integrity": "sha512-R4A8VfUBQY9WmAhuqY5tjHRf5fH2AAf6vqitBOE0y6u2PgHgqHSrhZmu78dIX3fVZtjqlwJNX1i2zwC3VpHtQQ==", + "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/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/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": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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/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/expect": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/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/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "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/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-tsconfig": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", + "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "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-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": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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": "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/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/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/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-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-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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-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": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "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/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isomorphic-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", + "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", + "dependencies": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, + "node_modules/isomorphic-fetch/node_modules/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "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.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.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.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", + "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==", + "dev": true, + "dependencies": { + "@jest/core": "^29.5.0", + "@jest/types": "^29.5.0", + "import-local": "^3.0.2", + "jest-cli": "^29.5.0" + }, + "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.5.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", + "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.5.0.tgz", + "integrity": "sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.5.0", + "@jest/expect": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.5.0", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.5.0", + "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-cli": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.5.0.tgz", + "integrity": "sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "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-config": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.5.0.tgz", + "integrity": "sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.5.0", + "@jest/types": "^29.5.0", + "babel-jest": "^29.5.0", + "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.5.0", + "jest-environment-node": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-runner": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.5.0", + "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-diff": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", + "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.4.3", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", + "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", + "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.5.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.5.0.tgz", + "integrity": "sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.3", + "jest-util": "^29.5.0", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz", + "integrity": "sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.5.0", + "@jest/fake-timers": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-mock": "^29.5.0", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", + "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.5.0.tgz", + "integrity": "sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@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.4.3", + "jest-util": "^29.5.0", + "jest-worker": "^29.5.0", + "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.5.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz", + "integrity": "sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", + "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.5.0", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", + "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.5.0", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz", + "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-util": "^29.5.0" + }, + "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.4.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", + "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.5.0.tgz", + "integrity": "sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "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.5.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz", + "integrity": "sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.4.3", + "jest-snapshot": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.5.0.tgz", + "integrity": "sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.5.0", + "@jest/environment": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.4.3", + "jest-environment-node": "^29.5.0", + "jest-haste-map": "^29.5.0", + "jest-leak-detector": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-resolve": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-util": "^29.5.0", + "jest-watcher": "^29.5.0", + "jest-worker": "^29.5.0", + "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-runtime": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.5.0.tgz", + "integrity": "sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.5.0", + "@jest/fake-timers": "^29.5.0", + "@jest/globals": "^29.5.0", + "@jest/source-map": "^29.4.3", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@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.5.0", + "jest-message-util": "^29.5.0", + "jest-mock": "^29.5.0", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.5.0.tgz", + "integrity": "sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==", + "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/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.5.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.5.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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.5.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", + "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@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-validate": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.5.0.tgz", + "integrity": "sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.3", + "leven": "^3.1.0", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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-watcher": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.5.0.tgz", + "integrity": "sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.5.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz", + "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.5.0", + "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/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/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/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/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==" + }, + "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/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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/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": ">= 8" + } + }, + "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": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.3.tgz", + "integrity": "sha512-KgUb15Oorc0NEKPbvfa0wRU+PItIEZmiv+pyAO2i0oTIVTJhlzMclU7w4RXWQrSOVH5ax/p/CkIO7KI4OyFJTQ==", + "funding": [ + "https://github.com/sponsors/broofa" + ], + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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/mkdirp": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", + "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", + "dev": true, + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/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/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.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "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==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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==", + "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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, + "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==", + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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/peggy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/peggy/-/peggy-3.0.2.tgz", + "integrity": "sha512-n7chtCbEoGYRwZZ0i/O3t1cPr6o+d9Xx4Zwy2LYfzv0vjchMBU0tO+qYYyvZloBPcgRgzYvALzGWHe609JjEpg==", + "dev": true, + "dependencies": { + "commander": "^10.0.0", + "source-map-generator": "0.8.0" + }, + "bin": { + "peggy": "bin/peggy.js" + }, + "engines": { + "node": ">=14" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "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==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", + "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.4.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "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_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pure-rand": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.1.tgz", + "integrity": "sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "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/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 + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "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, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "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": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "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": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "engines": { + "node": ">=8" + } + }, + "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/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-generator": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/source-map-generator/-/source-map-generator-0.8.0.tgz", + "integrity": "sha512-psgxdGMwl5MZM9S3FWee4EgsEaIjahYV5AzGnwUvPhWeITz/j6rKpysQHlQ4USdxvINlb8lKfWGIXwfkrgtqkA==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "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/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/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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": ">=8" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "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-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": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/ts-jest": { + "version": "29.0.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.5.tgz", + "integrity": "sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA==", + "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.x", + "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" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "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==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "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/ts-matches": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-5.5.1.tgz", + "integrity": "sha512-UFYaKgfqlg9FROK7bdpYqFwG1CJvP4kOJdjXuWoqxo9jCmANoDw1GxkSCpJgoTeIiSTaTH5Qr1klSspb8c+ydg==" + }, + "node_modules/ts-morph": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-18.0.0.tgz", + "integrity": "sha512-Kg5u0mk19PIIe4islUI/HWRvm9bC1lHejK4S0oh1zaZ77TMZAEmQC0sHQYiu2RgCQFZKXz1fMVi/7nOOeirznA==", + "dev": true, + "dependencies": { + "@ts-morph/common": "~0.19.0", + "code-block-writer": "^12.0.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-pegjs": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ts-pegjs/-/ts-pegjs-4.2.1.tgz", + "integrity": "sha512-mK/O2pu6lzWUeKpEMA/wsa0GdYblfjJI1y0s0GqH6xCTvugQDOWPJbm5rY6AHivpZICuXIriCb+a7Cflbdtc2w==", + "dev": true, + "dependencies": { + "prettier": "^2.8.8", + "ts-morph": "^18.0.0" + }, + "bin": { + "tspegjs": "dist/cli.mjs" + }, + "peerDependencies": { + "peggy": "^3.0.2" + } + }, + "node_modules/ts-pegjs/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": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/tsx": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.7.1.tgz", + "integrity": "sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==", + "dev": true, + "dependencies": { + "esbuild": "~0.19.10", + "get-tsconfig": "^4.7.2" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/tsx/node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "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==", + "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" + } + }, + "node_modules/v8-to-istanbul/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/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": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-fetch": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", + "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" + }, + "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_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" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "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==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/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/wrap-ansi/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/wrap-ansi/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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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/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/yaml": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", + "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "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/yargs/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/yargs/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/yargs/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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/sdk/package.json b/sdk/package/package.json similarity index 80% rename from sdk/package.json rename to sdk/package/package.json index f3af7ff1b..530a22053 100644 --- a/sdk/package.json +++ b/sdk/package/package.json @@ -2,22 +2,22 @@ "name": "@start9labs/start-sdk", "version": "0.3.6-alpha8", "description": "Software development kit to facilitate packaging services for StartOS", - "main": "./cjs/lib/index.js", - "types": "./cjs/lib/index.d.ts", - "module": "./mjs/lib/index.js", - "browser": "./mjs/lib/index.browser.js", + "main": "./package/lib/index.js", + "types": "./package/lib/index.d.ts", "sideEffects": true, "typesVersion": { ">=3.1": { "*": [ - "cjs/lib/*" + "package/lib/*", + "base/lib/*" ] } }, "scripts": { "test": "jest -c ./jest.config.js --coverage", - "buildOutput": "ts-node --project ./tsconfig-cjs.json ./lib/test/makeOutput.ts && npx prettier --write '**/*.ts'", - "check": "tsc --noEmit" + "buildOutput": "ts-node ./lib/test/makeOutput.ts && npx prettier --write '**/*.ts'", + "check": "tsc --noEmit", + "tsc": "tsc" }, "repository": { "type": "git", diff --git a/sdk/scripts/oldSpecToBuilder.ts b/sdk/package/scripts/oldSpecToBuilder.ts similarity index 94% rename from sdk/scripts/oldSpecToBuilder.ts rename to sdk/package/scripts/oldSpecToBuilder.ts index 6dd726e1b..04128f2bd 100644 --- a/sdk/scripts/oldSpecToBuilder.ts +++ b/sdk/package/scripts/oldSpecToBuilder.ts @@ -33,18 +33,18 @@ export default async function makeFileContentFromOld( const outputLines: string[] = [] outputLines.push(` import { sdk } from "${StartSdk}" -const {Config, List, Value, Variants} = sdk +const {InputSpec, List, Value, Variants} = sdk `) const data = await inputData - const namedConsts = new Set(["Config", "Value", "List"]) - const configName = newConst("configSpec", convertInputSpec(data)) - const configMatcherName = newConst( - "matchConfigSpec", - `${configName}.validator`, + const namedConsts = new Set(["InputSpec", "Value", "List"]) + const inputSpecName = newConst("inputSpecSpec", convertInputSpec(data)) + const inputSpecMatcherName = newConst( + "matchInputSpecSpec", + `${inputSpecName}.validator`, ) outputLines.push( - `export type ConfigSpec = typeof ${configMatcherName}._TYPE;`, + `export type InputSpecSpec = typeof ${inputSpecMatcherName}._TYPE;`, ) return outputLines.join("\n") @@ -71,7 +71,7 @@ const {Config, List, Value, Variants} = sdk } function convertInputSpec(data: any) { - return `Config.of(${convertInputSpecInner(data)})` + return `InputSpec.of(${convertInputSpecInner(data)})` } function convertValueSpec(value: any): string { switch (value.type) { @@ -195,7 +195,6 @@ const {Config, List, Value, Variants} = sdk return `Value.object({ name: ${JSON.stringify(value.name || null)}, description: ${JSON.stringify(value.description || null)}, - warning: ${JSON.stringify(value.warning || null)}, }, ${specName})` } case "union": { @@ -319,7 +318,6 @@ const {Config, List, Value, Variants} = sdk maxLength: ${JSON.stringify(null)}, default: ${JSON.stringify(value.default || null)}, description: ${JSON.stringify(value.description || null)}, - warning: ${JSON.stringify(value.warning || null)}, }, { spec: ${specName}, displayAs: ${JSON.stringify(value?.spec?.["display-as"] || null)}, @@ -352,10 +350,10 @@ const {Config, List, Value, Variants} = sdk }, ${variants}) `, ) - const listConfig = maybeNewConst( - value.name + "_list_config", + const listInputSpec = maybeNewConst( + value.name + "_list_inputSpec", ` - Config.of({ + InputSpec.of({ "union": ${unionValueName} }) `, @@ -368,7 +366,7 @@ const {Config, List, Value, Variants} = sdk description: ${JSON.stringify(value.description || null)}, warning: ${JSON.stringify(value.warning || null)}, }, { - spec: ${listConfig}, + spec: ${listInputSpec}, displayAs: ${JSON.stringify(value?.spec?.["display-as"] || null)}, uniqueBy: ${JSON.stringify(value?.spec?.["unique-by"] || null)}, })` @@ -407,7 +405,7 @@ function rangeToTodoComment(range: string | undefined) { } // oldSpecToBuilder( -// "./config.ts", -// // Put config here +// "./inputSpec.ts", +// // Put inputSpec here // {}, // ) diff --git a/sdk/package/tsconfig.json b/sdk/package/tsconfig.json new file mode 100644 index 000000000..7b4d4f7d8 --- /dev/null +++ b/sdk/package/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "strict": true, + "preserveConstEnums": true, + "sourceMap": true, + "pretty": true, + "declaration": true, + "noImplicitAny": true, + "esModuleInterop": true, + "types": ["node", "jest"], + "moduleResolution": "node", + "skipLibCheck": true, + "module": "commonjs", + "outDir": "../dist", + "target": "es2018" + }, + "include": ["lib/**/*", "../base/lib/util/Hostname.ts"], + "exclude": ["lib/**/*.spec.ts", "lib/**/*.gen.ts", "list", "node_modules"] +} diff --git a/sdk/tsconfig-cjs.json b/sdk/tsconfig-cjs.json deleted file mode 100644 index 8413cf248..000000000 --- a/sdk/tsconfig-cjs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig-base.json", - "compilerOptions": { - "module": "commonjs", - "outDir": "dist/cjs", - "target": "es2018" - } -} diff --git a/sdk/tsconfig.json b/sdk/tsconfig.json deleted file mode 100644 index 8ae7d62a8..000000000 --- a/sdk/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig-base.json", - "compilerOptions": { - "module": "esnext", - "outDir": "dist/mjs", - "target": "esnext" - } -} diff --git a/web/README.md b/web/README.md index 213113775..7665a26a6 100644 --- a/web/README.md +++ b/web/README.md @@ -3,33 +3,37 @@ StartOS web UIs are written in [Angular/Typescript](https://angular.io/docs) and leverage the [Ionic Framework](https://ionicframework.com/) component library. StartOS conditionally serves one of four Web UIs, depending on the state of the system and user choice. + - **install-wizard** - UI for installing StartOS, served on localhost. - **setup-wizard** - UI for setting up StartOS, served on start.local. -- **diagnostic-ui** - UI to display any error during server initialization, served on start.local. - **ui** - primary UI for administering StartOS, served on various hosts unique to the instance. Additionally, there are two libraries for shared code: + - **marketplace** - library code shared between the StartOS UI and Start9's [brochure marketplace](https://github.com/Start9Labs/brochure-marketplace). - **shared** - library code shared between the various web UIs and marketplace lib. ## Environment Setup #### Install NodeJS and NPM + - [Install nodejs](https://nodejs.org/en/) - [Install npm](https://www.npmjs.com/get-npm) #### Check that your versions match the ones below + ```sh node --version -v18.15.0 +v20.17.0 npm --version -v8.0.0 +v10.8.2 ``` #### Install and enable the Prettier extension for your text editor #### Clone StartOS and load the PatchDB submodule if you have not already + ```sh git clone https://github.com/Start9Labs/start-os.git cd start-os @@ -37,6 +41,7 @@ git submodule update --init --recursive ``` #### Move to web directory and install dependencies + ```sh cd web npm i @@ -44,6 +49,7 @@ npm run build:deps ``` #### Copy `config-sample.json` to a new file `config.json`. + ```sh cp config-sample.json config.json ``` @@ -59,10 +65,10 @@ You can develop using mocks (recommended to start) or against a live server. Eit ### Using mocks #### Start the standard development server + ```sh npm run start:install-wiz npm run start:setup -npm run start:dui npm run start:ui ``` @@ -71,6 +77,7 @@ npm run start:ui #### In `config.json`, set "useMocks" to `false` #### Copy `proxy.conf-sample.json` to a new file `proxy.conf.json` + ```sh cp proxy.conf-sample.json proxy.conf.json ``` @@ -78,6 +85,7 @@ cp proxy.conf-sample.json proxy.conf.json #### Replace every instance of "\\" with the hostname of your remote server #### Start the proxy development server + ```sh npm run start:ui:proxy ``` diff --git a/web/package-lock.json b/web/package-lock.json index 7ceeb4399..2f05ddab3 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -28,7 +28,7 @@ "@noble/hashes": "^1.4.0", "@start9labs/argon2": "^0.2.2", "@start9labs/emver": "^0.1.5", - "@start9labs/start-sdk": "file:../sdk/dist", + "@start9labs/start-sdk": "file:../sdk/baseDist", "@taiga-ui/addon-charts": "3.86.0", "@taiga-ui/cdk": "3.86.0", "@taiga-ui/core": "3.86.0", @@ -114,9 +114,36 @@ "rxjs": ">=7.0.0" } }, + "../sdk/baseDist": { + "name": "@start9labs/start-sdk-base", + "license": "MIT", + "dependencies": { + "@iarna/toml": "^2.2.5", + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0", + "isomorphic-fetch": "^3.0.0", + "lodash.merge": "^4.6.2", + "mime": "^4.0.3", + "ts-matches": "^5.5.1", + "yaml": "^2.2.2" + }, + "devDependencies": { + "@types/jest": "^29.4.0", + "@types/lodash.merge": "^4.6.2", + "jest": "^29.4.3", + "peggy": "^3.0.2", + "prettier": "^3.2.5", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.1", + "ts-pegjs": "^4.2.1", + "tsx": "^4.7.1", + "typescript": "^5.0.4" + } + }, "../sdk/dist": { "name": "@start9labs/start-sdk", "version": "0.3.6-alpha8", + "extraneous": true, "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", @@ -4116,7 +4143,7 @@ "integrity": "sha512-1dhiG03VkfEwSLx/JPKVms6srAbYFQgwfSGhwpUKMDliMXuAHGVaueStmqzVxn3JpH/HEVz0QW8w/PXHqjdiIg==" }, "node_modules/@start9labs/start-sdk": { - "resolved": "../sdk/dist", + "resolved": "../sdk/baseDist", "link": true }, "node_modules/@stencil/core": { diff --git a/web/package.json b/web/package.json index 945dde7cf..c73d93859 100644 --- a/web/package.json +++ b/web/package.json @@ -51,7 +51,7 @@ "@noble/hashes": "^1.4.0", "@start9labs/argon2": "^0.2.2", "@start9labs/emver": "^0.1.5", - "@start9labs/start-sdk": "file:../sdk/dist", + "@start9labs/start-sdk": "file:../sdk/baseDist", "@taiga-ui/addon-charts": "3.86.0", "@taiga-ui/cdk": "3.86.0", "@taiga-ui/core": "3.86.0", diff --git a/web/projects/setup-wizard/src/app/pages/loading/loading.page.html b/web/projects/setup-wizard/src/app/pages/loading/loading.page.html index 94a666223..942d667a3 100644 --- a/web/projects/setup-wizard/src/app/pages/loading/loading.page.html +++ b/web/projects/setup-wizard/src/app/pages/loading/loading.page.html @@ -15,7 +15,7 @@ [style.margin]="'1rem auto'" [attr.value]="progress.total" > -

{{ progress.message }}

+

{{ progress.message }}

diff --git a/web/projects/ui/src/app/components/backup-drives/backup-drives.component.ts b/web/projects/ui/src/app/components/backup-drives/backup-drives.component.ts index a4b272c46..743d3546b 100644 --- a/web/projects/ui/src/app/components/backup-drives/backup-drives.component.ts +++ b/web/projects/ui/src/app/components/backup-drives/backup-drives.component.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core' import { ActionSheetController, AlertController } from '@ionic/angular' import { ErrorService, LoadingService } from '@start9labs/shared' -import { CB } from '@start9labs/start-sdk' +import { ISB } from '@start9labs/start-sdk' import { CifsBackupTarget, DiskBackupTarget, @@ -261,8 +261,8 @@ export class BackupDrivesStatusComponent { @Input() hasAnyBackup!: boolean } -const cifsSpec = CB.Config.of({ - hostname: CB.Value.text({ +const cifsSpec = ISB.InputSpec.of({ + hostname: ISB.Value.text({ name: 'Hostname', description: 'The hostname of your target device on the Local Area Network.', @@ -271,19 +271,19 @@ const cifsSpec = CB.Config.of({ required: { default: null }, patterns: [], }), - path: CB.Value.text({ + path: ISB.Value.text({ name: 'Path', description: `On Windows, this is the fully qualified path to the shared folder, (e.g. /Desktop/my-folder).\n\n On Linux and Mac, this is the literal name of the shared folder (e.g. my-shared-folder).`, placeholder: 'e.g. my-shared-folder or /Desktop/my-folder', required: { default: null }, }), - username: CB.Value.text({ + username: ISB.Value.text({ name: 'Username', description: `On Linux, this is the samba username you created when sharing the folder.\n\n On Mac and Windows, this is the username of the user who is sharing the folder.`, required: { default: null }, placeholder: 'My Network Folder', }), - password: CB.Value.text({ + password: ISB.Value.text({ name: 'Password', description: `On Linux, this is the samba password you created when sharing the folder.\n\n On Mac and Windows, this is the password of the user who is sharing the folder.`, required: false, diff --git a/web/projects/ui/src/app/components/form.component.ts b/web/projects/ui/src/app/components/form.component.ts index 830e8de1e..386bd5f8e 100644 --- a/web/projects/ui/src/app/components/form.component.ts +++ b/web/projects/ui/src/app/components/form.component.ts @@ -8,8 +8,7 @@ import { } from '@angular/core' import { FormGroup, ReactiveFormsModule } from '@angular/forms' import { RouterModule } from '@angular/router' -import { CT } from '@start9labs/start-sdk' - +import { IST } from '@start9labs/start-sdk' import { tuiMarkControlAsTouchedAndValidate, TuiValueChangesModule, @@ -30,10 +29,10 @@ export interface ActionButton { } export interface FormContext { - spec: CT.InputSpec + spec: IST.InputSpec buttons: ActionButton[] value?: T - patch?: Operation[] + operations?: Operation[] } @Component({ @@ -112,7 +111,7 @@ export class FormComponent> implements OnInit { @Input() spec = this.context?.data.spec || {} @Input() buttons = this.context?.data.buttons || [] - @Input() patch = this.context?.data.patch || [] + @Input() operations = this.context?.data.operations || [] @Input() value?: T = this.context?.data.value form = new FormGroup({}) @@ -120,7 +119,7 @@ export class FormComponent> implements OnInit { ngOnInit() { this.dialogFormService.markAsPristine() this.form = this.formService.createForm(this.spec, this.value) - this.process(this.patch) + this.process(this.operations) } onReset() { @@ -149,8 +148,8 @@ export class FormComponent> implements OnInit { this.context?.$implicit.complete() } - private process(patch: Operation[]) { - patch.forEach(({ op, path }) => { + private process(operations: Operation[]) { + operations.forEach(({ op, path }) => { const control = this.form.get(path.substring(1).split('/')) if (!control || !control.parent) return diff --git a/web/projects/ui/src/app/components/form/control.ts b/web/projects/ui/src/app/components/form/control.ts index 476826194..c77c76ecf 100644 --- a/web/projects/ui/src/app/components/form/control.ts +++ b/web/projects/ui/src/app/components/form/control.ts @@ -1,8 +1,8 @@ import { inject } from '@angular/core' import { FormControlComponent } from './form-control/form-control.component' -import { CT } from '@start9labs/start-sdk' +import { IST } from '@start9labs/start-sdk' -export abstract class Control { +export abstract class Control, Value> { private readonly control: FormControlComponent = inject(FormControlComponent) diff --git a/web/projects/ui/src/app/components/form/filter-hidden.pipe.ts b/web/projects/ui/src/app/components/form/filter-hidden.pipe.ts new file mode 100644 index 000000000..84666a2c9 --- /dev/null +++ b/web/projects/ui/src/app/components/form/filter-hidden.pipe.ts @@ -0,0 +1,15 @@ +import { Pipe, PipeTransform } from '@angular/core' +import { IST } from '@start9labs/start-sdk' +import { KeyValue } from '@angular/common' + +@Pipe({ + name: 'filterHidden', +}) +export class FilterHiddenPipe implements PipeTransform { + transform(value: KeyValue[]) { + return value.filter(x => x.value.type !== 'hidden') as KeyValue< + string, + Exclude + >[] + } +} diff --git a/web/projects/ui/src/app/components/form/form-array/form-array.component.ts b/web/projects/ui/src/app/components/form/form-array/form-array.component.ts index 11495d510..25242f826 100644 --- a/web/projects/ui/src/app/components/form/form-array/form-array.component.ts +++ b/web/projects/ui/src/app/components/form/form-array/form-array.component.ts @@ -8,7 +8,7 @@ import { tuiHeightCollapse, } from '@taiga-ui/core' import { TUI_PROMPT } from '@taiga-ui/kit' -import { CT } from '@start9labs/start-sdk' +import { IST } from '@start9labs/start-sdk' import { filter, takeUntil } from 'rxjs' import { FormService } from 'src/app/services/form.service' import { ERRORS } from '../form-group/form-group.component' @@ -22,7 +22,7 @@ import { ERRORS } from '../form-group/form-group.component' }) export class FormArrayComponent { @Input() - spec!: CT.ValueSpecList + spec!: IST.ValueSpecList @HostBinding('@tuiParentAnimation') readonly animation = { value: '', ...inject(TUI_ANIMATION_OPTIONS) } diff --git a/web/projects/ui/src/app/components/form/form-color/form-color.component.ts b/web/projects/ui/src/app/components/form/form-color/form-color.component.ts index 32a7c1c04..0f65f06ce 100644 --- a/web/projects/ui/src/app/components/form/form-color/form-color.component.ts +++ b/web/projects/ui/src/app/components/form/form-color/form-color.component.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core' -import { CT } from '@start9labs/start-sdk' +import { IST } from '@start9labs/start-sdk' import { Control } from '../control' import { MaskitoOptions } from '@maskito/core' @@ -8,7 +8,7 @@ import { MaskitoOptions } from '@maskito/core' templateUrl: './form-color.component.html', styleUrls: ['./form-color.component.scss'], }) -export class FormColorComponent extends Control { +export class FormColorComponent extends Control { readonly mask: MaskitoOptions = { mask: ['#', ...Array(6).fill(/[0-9a-f]/i)], } diff --git a/web/projects/ui/src/app/components/form/form-control/form-control.component.html b/web/projects/ui/src/app/components/form/form-control/form-control.component.html index 731d64a63..dd3cf2b89 100644 --- a/web/projects/ui/src/app/components/form/form-control/form-control.component.html +++ b/web/projects/ui/src/app/components/form/form-control/form-control.component.html @@ -36,4 +36,4 @@ Accept - + \ No newline at end of file diff --git a/web/projects/ui/src/app/components/form/form-control/form-control.component.ts b/web/projects/ui/src/app/components/form/form-control/form-control.component.ts index ec49bd084..9544188a6 100644 --- a/web/projects/ui/src/app/components/form/form-control/form-control.component.ts +++ b/web/projects/ui/src/app/components/form/form-control/form-control.component.ts @@ -13,7 +13,7 @@ import { TuiNotification, } from '@taiga-ui/core' import { filter, takeUntil } from 'rxjs' -import { CT } from '@start9labs/start-sdk' +import { IST } from '@start9labs/start-sdk' import { ERRORS } from '../form-group/form-group.component' import { FORM_CONTROL_PROVIDERS } from './form-control.providers' @@ -25,7 +25,7 @@ import { FORM_CONTROL_PROVIDERS } from './form-control.providers' providers: FORM_CONTROL_PROVIDERS, }) export class FormControlComponent< - T extends CT.ValueSpec, + T extends Exclude, V, > extends AbstractTuiNullableControl { @Input() diff --git a/web/projects/ui/src/app/components/form/form-control/form-control.providers.ts b/web/projects/ui/src/app/components/form/form-control/form-control.providers.ts index f065f86cb..62e1ff6aa 100644 --- a/web/projects/ui/src/app/components/form/form-control/form-control.providers.ts +++ b/web/projects/ui/src/app/components/form/form-control/form-control.providers.ts @@ -1,6 +1,6 @@ import { forwardRef, Provider } from '@angular/core' import { TUI_VALIDATION_ERRORS } from '@taiga-ui/kit' -import { CT } from '@start9labs/start-sdk' +import { IST } from '@start9labs/start-sdk' import { FormControlComponent } from './form-control.component' interface ValidatorsPatternError { @@ -12,7 +12,7 @@ export const FORM_CONTROL_PROVIDERS: Provider[] = [ { provide: TUI_VALIDATION_ERRORS, deps: [forwardRef(() => FormControlComponent)], - useFactory: (control: FormControlComponent) => ({ + useFactory: (control: FormControlComponent, string>) => ({ required: 'Required', pattern: ({ requiredPattern }: ValidatorsPatternError) => ('patterns' in control.spec && diff --git a/web/projects/ui/src/app/components/form/form-datetime/form-datetime.component.ts b/web/projects/ui/src/app/components/form/form-datetime/form-datetime.component.ts index e09b22d24..fc3acecd0 100644 --- a/web/projects/ui/src/app/components/form/form-datetime/form-datetime.component.ts +++ b/web/projects/ui/src/app/components/form/form-datetime/form-datetime.component.ts @@ -6,7 +6,7 @@ import { tuiPure, TuiTime, } from '@taiga-ui/cdk' -import { CT } from '@start9labs/start-sdk' +import { IST } from '@start9labs/start-sdk' import { Control } from '../control' @Component({ @@ -14,7 +14,7 @@ import { Control } from '../control' templateUrl: './form-datetime.component.html', }) export class FormDatetimeComponent extends Control< - CT.ValueSpecDatetime, + IST.ValueSpecDatetime, string > { readonly min = TUI_FIRST_DAY diff --git a/web/projects/ui/src/app/components/form/form-group/form-group.component.html b/web/projects/ui/src/app/components/form/form-group/form-group.component.html index 1c4f8301a..65975e970 100644 --- a/web/projects/ui/src/app/components/form/form-group/form-group.component.html +++ b/web/projects/ui/src/app/components/form/form-group/form-group.component.html @@ -1,5 +1,5 @@ { private readonly inverted = invert(this.spec.values) diff --git a/web/projects/ui/src/app/components/form/form-number/form-number.component.ts b/web/projects/ui/src/app/components/form/form-number/form-number.component.ts index a930b1614..b07858207 100644 --- a/web/projects/ui/src/app/components/form/form-number/form-number.component.ts +++ b/web/projects/ui/src/app/components/form/form-number/form-number.component.ts @@ -1,11 +1,11 @@ import { Component } from '@angular/core' -import { CT } from '@start9labs/start-sdk' +import { IST } from '@start9labs/start-sdk' import { Control } from '../control' @Component({ selector: 'form-number', templateUrl: './form-number.component.html', }) -export class FormNumberComponent extends Control { +export class FormNumberComponent extends Control { protected readonly Infinity = Infinity } diff --git a/web/projects/ui/src/app/components/form/form-object/form-object.component.ts b/web/projects/ui/src/app/components/form/form-object/form-object.component.ts index b1aa507cf..a036c1e43 100644 --- a/web/projects/ui/src/app/components/form/form-object/form-object.component.ts +++ b/web/projects/ui/src/app/components/form/form-object/form-object.component.ts @@ -7,7 +7,7 @@ import { Output, } from '@angular/core' import { ControlContainer } from '@angular/forms' -import { CT } from '@start9labs/start-sdk' +import { IST } from '@start9labs/start-sdk' @Component({ selector: 'form-object', @@ -17,7 +17,7 @@ import { CT } from '@start9labs/start-sdk' }) export class FormObjectComponent { @Input() - spec!: CT.ValueSpecObject + spec!: IST.ValueSpecObject @Input() open = false diff --git a/web/projects/ui/src/app/components/form/form-select/form-select.component.ts b/web/projects/ui/src/app/components/form/form-select/form-select.component.ts index ccbbccae8..ac478a4d1 100644 --- a/web/projects/ui/src/app/components/form/form-select/form-select.component.ts +++ b/web/projects/ui/src/app/components/form/form-select/form-select.component.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core' -import { CT } from '@start9labs/start-sdk' +import { IST } from '@start9labs/start-sdk' import { invert } from '@start9labs/shared' import { Control } from '../control' @@ -7,7 +7,7 @@ import { Control } from '../control' selector: 'form-select', templateUrl: './form-select.component.html', }) -export class FormSelectComponent extends Control { +export class FormSelectComponent extends Control { private readonly inverted = invert(this.spec.values) readonly items = Object.values(this.spec.values) diff --git a/web/projects/ui/src/app/components/form/form-text/form-text.component.ts b/web/projects/ui/src/app/components/form/form-text/form-text.component.ts index 8a5ac86f0..703570dbc 100644 --- a/web/projects/ui/src/app/components/form/form-text/form-text.component.ts +++ b/web/projects/ui/src/app/components/form/form-text/form-text.component.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core' -import { CT, utils } from '@start9labs/start-sdk' +import { IST, utils } from '@start9labs/start-sdk' import { Control } from '../control' @Component({ @@ -7,7 +7,7 @@ import { Control } from '../control' templateUrl: './form-text.component.html', styleUrls: ['./form-text.component.scss'], }) -export class FormTextComponent extends Control { +export class FormTextComponent extends Control { masked = true generate() { diff --git a/web/projects/ui/src/app/components/form/form-textarea/form-textarea.component.ts b/web/projects/ui/src/app/components/form/form-textarea/form-textarea.component.ts index b32685c21..0c2bd054d 100644 --- a/web/projects/ui/src/app/components/form/form-textarea/form-textarea.component.ts +++ b/web/projects/ui/src/app/components/form/form-textarea/form-textarea.component.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core' -import { CT } from '@start9labs/start-sdk' +import { IST } from '@start9labs/start-sdk' import { Control } from '../control' @Component({ @@ -7,6 +7,6 @@ import { Control } from '../control' templateUrl: './form-textarea.component.html', }) export class FormTextareaComponent extends Control< - CT.ValueSpecTextarea, + IST.ValueSpecTextarea, string > {} diff --git a/web/projects/ui/src/app/components/form/form-toggle/form-toggle.component.ts b/web/projects/ui/src/app/components/form/form-toggle/form-toggle.component.ts index 6a3c0196f..fd4f18218 100644 --- a/web/projects/ui/src/app/components/form/form-toggle/form-toggle.component.ts +++ b/web/projects/ui/src/app/components/form/form-toggle/form-toggle.component.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core' -import { CT } from '@start9labs/start-sdk' +import { IST } from '@start9labs/start-sdk' import { Control } from '../control' @Component({ @@ -7,4 +7,7 @@ import { Control } from '../control' templateUrl: './form-toggle.component.html', host: { class: 'g-toggle' }, }) -export class FormToggleComponent extends Control {} +export class FormToggleComponent extends Control< + IST.ValueSpecToggle, + boolean +> {} diff --git a/web/projects/ui/src/app/components/form/form-union/form-union.component.ts b/web/projects/ui/src/app/components/form/form-union/form-union.component.ts index 2c164be48..83c69bd6d 100644 --- a/web/projects/ui/src/app/components/form/form-union/form-union.component.ts +++ b/web/projects/ui/src/app/components/form/form-union/form-union.component.ts @@ -6,7 +6,7 @@ import { OnChanges, } from '@angular/core' import { ControlContainer, FormGroupName } from '@angular/forms' -import { CT } from '@start9labs/start-sdk' +import { IST } from '@start9labs/start-sdk' import { FormService } from 'src/app/services/form.service' import { tuiPure } from '@taiga-ui/cdk' @@ -24,9 +24,9 @@ import { tuiPure } from '@taiga-ui/cdk' }) export class FormUnionComponent implements OnChanges { @Input() - spec!: CT.ValueSpecUnion + spec!: IST.ValueSpecUnion - selectSpec!: CT.ValueSpecSelect + selectSpec!: IST.ValueSpecSelect private readonly form = inject(FormGroupName) private readonly formService = inject(FormService) diff --git a/web/projects/ui/src/app/components/form/form.module.ts b/web/projects/ui/src/app/components/form/form.module.ts index 5417309a9..f57f6ef46 100644 --- a/web/projects/ui/src/app/components/form/form.module.ts +++ b/web/projects/ui/src/app/components/form/form.module.ts @@ -50,6 +50,7 @@ import { ControlDirective } from './control.directive' import { FormColorComponent } from './form-color/form-color.component' import { FormDatetimeComponent } from './form-datetime/form-datetime.component' import { HintPipe } from './hint.pipe' +import { FilterHiddenPipe } from './filter-hidden.pipe' @NgModule({ imports: [ @@ -101,6 +102,7 @@ import { HintPipe } from './hint.pipe' MustachePipe, HintPipe, ControlDirective, + FilterHiddenPipe, ], exports: [FormGroupComponent], }) diff --git a/web/projects/ui/src/app/components/form/hint.pipe.ts b/web/projects/ui/src/app/components/form/hint.pipe.ts index b5a730661..f03d9577f 100644 --- a/web/projects/ui/src/app/components/form/hint.pipe.ts +++ b/web/projects/ui/src/app/components/form/hint.pipe.ts @@ -1,11 +1,11 @@ import { Pipe, PipeTransform } from '@angular/core' -import { CT } from '@start9labs/start-sdk' +import { IST } from '@start9labs/start-sdk' @Pipe({ name: 'hint', }) export class HintPipe implements PipeTransform { - transform(spec: CT.ValueSpec): string { + transform(spec: Exclude): string { const hint = [] if (spec.description) { diff --git a/web/projects/ui/src/app/modals/config-dep.component.ts b/web/projects/ui/src/app/modals/action-dep.component.ts similarity index 78% rename from web/projects/ui/src/app/modals/config-dep.component.ts rename to web/projects/ui/src/app/modals/action-dep.component.ts index 14becf2e8..8eeb9faa8 100644 --- a/web/projects/ui/src/app/modals/config-dep.component.ts +++ b/web/projects/ui/src/app/modals/action-dep.component.ts @@ -2,50 +2,49 @@ import { ChangeDetectionStrategy, Component, Input, - OnChanges, + OnInit, } from '@angular/core' -import { compare, getValueByPointer, Operation } from 'fast-json-patch' +import { getValueByPointer, Operation } from 'fast-json-patch' import { isObject } from '@start9labs/shared' import { tuiIsNumber } from '@taiga-ui/cdk' import { CommonModule } from '@angular/common' import { TuiNotificationModule } from '@taiga-ui/core' @Component({ - selector: 'config-dep', + selector: 'action-dep', template: `

- {{ package }} + {{ pkgTitle }}

- The following modifications have been made to {{ package }} to satisfy - {{ dep }}: + The following modifications have been made to {{ pkgTitle }} to satisfy + {{ depTitle }}:
- To accept these modifications, click "Save".
`, standalone: true, imports: [CommonModule, TuiNotificationModule], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class ConfigDepComponent implements OnChanges { +export class ActionDepComponent implements OnInit { @Input() - package = '' + pkgTitle = '' @Input() - dep = '' + depTitle = '' @Input() - original: object = {} + originalValue: object = {} @Input() - value: object = {} + operations: Operation[] = [] diff: string[] = [] - ngOnChanges() { - this.diff = compare(this.original, this.value).map( + ngOnInit() { + this.diff = this.operations.map( op => `${this.getPath(op)}: ${this.getMessage(op)}`, ) } @@ -82,7 +81,7 @@ export class ConfigDepComponent implements OnChanges { } private getOldValue(path: any): string { - const val = getValueByPointer(this.original, path) + const val = getValueByPointer(this.originalValue, path) if (['string', 'number', 'boolean'].includes(typeof val)) { return val } else if (isObject(val)) { diff --git a/web/projects/ui/src/app/modals/action-input.component.ts b/web/projects/ui/src/app/modals/action-input.component.ts new file mode 100644 index 000000000..494dfcdda --- /dev/null +++ b/web/projects/ui/src/app/modals/action-input.component.ts @@ -0,0 +1,208 @@ +import { CommonModule } from '@angular/common' +import { Component, Inject } from '@angular/core' +import { getErrorMessage } from '@start9labs/shared' +import { T, utils } from '@start9labs/start-sdk' +import { TuiButtonModule } from '@taiga-ui/experimental' +import { + TuiDialogContext, + TuiDialogService, + TuiLoaderModule, + TuiModeModule, + TuiNotificationModule, +} from '@taiga-ui/core' +import { TUI_PROMPT, TuiPromptData } from '@taiga-ui/kit' +import { POLYMORPHEUS_CONTEXT } from '@tinkoff/ng-polymorpheus' +import { compare } from 'fast-json-patch' +import { PatchDB } from 'patch-db-client' +import { catchError, defer, EMPTY, endWith, firstValueFrom, map } from 'rxjs' +import { InvalidService } from 'src/app/components/form/invalid.service' +import { ActionDepComponent } from 'src/app/modals/action-dep.component' +import { UiPipeModule } from 'src/app/pipes/ui/ui.module' +import { ApiService } from 'src/app/services/api/embassy-api.service' +import { DataModel } from 'src/app/services/patch-db/data-model' +import { getAllPackages, getManifest } from 'src/app/util/get-package-data' +import * as json from 'fast-json-patch' +import { ActionService } from '../services/action.service' +import { ActionButton, FormComponent } from '../components/form.component' + +export interface PackageActionData { + readonly pkgInfo: { + id: string + title: string + } + readonly actionInfo: { + id: string + warning: string | null + } + readonly dependentInfo?: { + title: string + request: T.ActionRequest + } +} + +@Component({ + template: ` + + +
+
+ + + +
+
+ + + + + + +
+
+ + + + + `, + styles: [ + ` + tui-notification { + font-size: 1rem; + margin-bottom: 1rem; + } + `, + ], + standalone: true, + imports: [ + CommonModule, + TuiLoaderModule, + TuiNotificationModule, + TuiButtonModule, + TuiModeModule, + ActionDepComponent, + UiPipeModule, + FormComponent, + ], + providers: [InvalidService], +}) +export class ActionInputModal { + readonly actionId = this.context.data.actionInfo.id + readonly warning = this.context.data.actionInfo.warning + readonly pkgInfo = this.context.data.pkgInfo + readonly dependentInfo = this.context.data.dependentInfo + + buttons: ActionButton[] = [ + { + text: 'Submit', + handler: value => this.execute(value), + }, + ] + + error = '' + + res$ = defer(() => + this.api.getActionInput({ + packageId: this.pkgInfo.id, + actionId: this.actionId, + }), + ).pipe( + map(res => { + const originalValue = res.value || {} + + return { + spec: res.spec, + originalValue, + operations: this.dependentInfo?.request.input + ? compare( + originalValue, + utils.deepMerge( + originalValue, + this.dependentInfo.request.input.value, + ) as object, + ) + : null, + } + }), + catchError(e => { + this.error = String(getErrorMessage(e)) + return EMPTY + }), + ) + + constructor( + @Inject(POLYMORPHEUS_CONTEXT) + private readonly context: TuiDialogContext, + private readonly dialogs: TuiDialogService, + private readonly api: ApiService, + private readonly patch: PatchDB, + private readonly actionService: ActionService, + ) {} + + async execute(input: object) { + if (await this.checkConflicts(input)) { + const res = await firstValueFrom(this.res$) + + return this.actionService.execute(this.pkgInfo.id, this.actionId, { + prev: { + spec: res.spec, + value: res.originalValue, + }, + curr: input, + }) + } + } + + private async checkConflicts(input: object): Promise { + const packages = await getAllPackages(this.patch) + + const breakages = Object.keys(packages) + .filter( + id => + id !== this.pkgInfo.id && + Object.values(packages[id].requestedActions).some( + ({ request, active }) => + !active && + request.packageId === this.pkgInfo.id && + request.actionId === this.actionId && + request.when?.condition === 'input-not-matches' && + request.input && + json + .compare(input, request.input) + .some(op => op.op === 'add' || op.op === 'replace'), + ), + ) + .map(id => id) + + if (!breakages.length) return true + + const message = + 'As a result of this change, the following services will no longer work properly and may crash:
    ' + const content = `${message}${breakages.map( + id => `
  • ${getManifest(packages[id]).title}
  • `, + )}
` + const data: TuiPromptData = { content, yes: 'Continue', no: 'Cancel' } + + return firstValueFrom( + this.dialogs.open(TUI_PROMPT, { data }).pipe(endWith(false)), + ) + } +} diff --git a/web/projects/ui/src/app/modals/action-success/action-success.page.ts b/web/projects/ui/src/app/modals/action-success/action-success.page.ts index 30c5c02cd..8912ed6ba 100644 --- a/web/projects/ui/src/app/modals/action-success/action-success.page.ts +++ b/web/projects/ui/src/app/modals/action-success/action-success.page.ts @@ -1,7 +1,7 @@ import { Component, Input } from '@angular/core' import { ModalController, ToastController } from '@ionic/angular' -import { ActionResponse } from 'src/app/services/api/api.types' import { copyToClipboard } from '@start9labs/shared' +import { T } from '@start9labs/start-sdk' @Component({ selector: 'action-success', @@ -10,7 +10,7 @@ import { copyToClipboard } from '@start9labs/shared' }) export class ActionSuccessPage { @Input() - actionRes!: ActionResponse + actionRes!: T.ActionResult constructor( private readonly modalCtrl: ModalController, diff --git a/web/projects/ui/src/app/modals/config.component.ts b/web/projects/ui/src/app/modals/config.component.ts deleted file mode 100644 index d0e8afa4d..000000000 --- a/web/projects/ui/src/app/modals/config.component.ts +++ /dev/null @@ -1,261 +0,0 @@ -import { CommonModule } from '@angular/common' -import { Component, Inject, ViewChild } from '@angular/core' -import { - ErrorService, - getErrorMessage, - isEmptyObject, - LoadingService, -} from '@start9labs/shared' -import { CT, T } from '@start9labs/start-sdk' -import { TuiButtonModule } from '@taiga-ui/experimental' -import { - TuiDialogContext, - TuiDialogService, - TuiLoaderModule, - TuiModeModule, - TuiNotificationModule, -} from '@taiga-ui/core' -import { TUI_PROMPT, TuiPromptData } from '@taiga-ui/kit' -import { POLYMORPHEUS_CONTEXT } from '@tinkoff/ng-polymorpheus' -import { compare, Operation } from 'fast-json-patch' -import { PatchDB } from 'patch-db-client' -import { endWith, firstValueFrom, Subscription } from 'rxjs' -import { ActionButton, FormComponent } from 'src/app/components/form.component' -import { InvalidService } from 'src/app/components/form/invalid.service' -import { ConfigDepComponent } from 'src/app/modals/config-dep.component' -import { UiPipeModule } from 'src/app/pipes/ui/ui.module' -import { ApiService } from 'src/app/services/api/embassy-api.service' -import { - DataModel, - PackageDataEntry, -} from 'src/app/services/patch-db/data-model' -import { - getAllPackages, - getManifest, - getPackage, -} from 'src/app/util/get-package-data' -import { hasCurrentDeps } from 'src/app/util/has-deps' -import { Breakages } from 'src/app/services/api/api.types' -import { DependentInfo } from 'src/app/types/dependent-info' - -export interface PackageConfigData { - readonly pkgId: string - readonly dependentInfo?: DependentInfo -} - -@Component({ - template: ` - - - -
-
- - - - {{ manifest.title }} has been automatically configured with recommended - defaults. Make whatever changes you want, then click "Save". - - - - - - No config options for {{ manifest.title }} {{ manifest.version }}. - - - - - - - `, - styles: [ - ` - tui-notification { - font-size: 1rem; - margin-bottom: 1rem; - } - `, - ], - standalone: true, - imports: [ - CommonModule, - FormComponent, - TuiLoaderModule, - TuiNotificationModule, - TuiButtonModule, - TuiModeModule, - ConfigDepComponent, - UiPipeModule, - ], - providers: [InvalidService], -}) -export class ConfigModal { - @ViewChild(FormComponent) - private readonly form?: FormComponent> - - readonly pkgId = this.context.data.pkgId - readonly dependentInfo = this.context.data.dependentInfo - - loadingError = '' - loadingText = this.dependentInfo - ? `Setting properties to accommodate ${this.dependentInfo.title}` - : 'Loading Config' - - pkg?: PackageDataEntry - spec: CT.InputSpec = {} - patch: Operation[] = [] - buttons: ActionButton[] = [ - { - text: 'Save', - handler: value => this.save(value), - }, - ] - - original: object | null = null - value: object | null = null - - constructor( - @Inject(POLYMORPHEUS_CONTEXT) - private readonly context: TuiDialogContext, - private readonly dialogs: TuiDialogService, - private readonly errorService: ErrorService, - private readonly loader: LoadingService, - private readonly embassyApi: ApiService, - private readonly patchDb: PatchDB, - ) {} - - get success(): boolean { - return ( - !!this.form && - !this.form.form.dirty && - !this.original && - !this.pkg?.status?.configured - ) - } - - async ngOnInit() { - try { - this.pkg = await getPackage(this.patchDb, this.pkgId) - - if (!this.pkg) { - this.loadingError = 'This service does not exist' - - return - } - - if (this.dependentInfo) { - const depConfig = await this.embassyApi.dryConfigureDependency({ - dependencyId: this.pkgId, - dependentId: this.dependentInfo.id, - }) - - this.original = depConfig.oldConfig - this.value = depConfig.newConfig || this.original - this.spec = depConfig.spec - this.patch = compare(this.original, this.value) - } else { - const { config, spec } = await this.embassyApi.getPackageConfig({ - id: this.pkgId, - }) - - this.original = config - this.value = config - this.spec = spec - } - } catch (e: any) { - this.loadingError = String(getErrorMessage(e)) - } finally { - this.loadingText = '' - } - } - - private async save(config: any) { - const loader = new Subscription() - - try { - if (hasCurrentDeps(this.pkgId, await getAllPackages(this.patchDb))) { - await this.configureDeps(config, loader) - } else { - await this.configure(config, loader) - } - } catch (e: any) { - this.errorService.handleError(e) - } finally { - loader.unsubscribe() - } - } - - private async configureDeps( - config: Record, - loader: Subscription, - ) { - loader.unsubscribe() - loader.closed = false - loader.add(this.loader.open('Checking dependent services...').subscribe()) - - const breakages = await this.embassyApi.drySetPackageConfig({ - id: this.pkgId, - config, - }) - - loader.unsubscribe() - loader.closed = false - - if (isEmptyObject(breakages) || (await this.approveBreakages(breakages))) { - await this.configure(config, loader) - } - } - - private async configure(config: Record, loader: Subscription) { - loader.unsubscribe() - loader.closed = false - loader.add(this.loader.open('Saving...').subscribe()) - - await this.embassyApi.setPackageConfig({ id: this.pkgId, config }) - this.context.$implicit.complete() - } - - private async approveBreakages(breakages: T.PackageId[]): Promise { - const packages = await getAllPackages(this.patchDb) - const message = - 'As a result of this change, the following services will no longer work properly and may crash:
    ' - const content = `${message}${breakages.map( - id => `
  • ${getManifest(packages[id]).title}
  • `, - )}
` - const data: TuiPromptData = { content, yes: 'Continue', no: 'Cancel' } - - return firstValueFrom( - this.dialogs.open(TUI_PROMPT, { data }).pipe(endWith(false)), - ) - } -} diff --git a/web/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.ts b/web/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.ts index dd8fe73eb..4c70d2837 100644 --- a/web/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.ts +++ b/web/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.ts @@ -7,7 +7,7 @@ import { sameUrl, toUrl, } from '@start9labs/shared' -import { CT } from '@start9labs/start-sdk' +import { IST } from '@start9labs/start-sdk' import { TuiDialogOptions, TuiDialogService } from '@taiga-ui/core' import { TuiButtonModule, @@ -182,7 +182,7 @@ export const MARKETPLACE_REGISTRY = new PolymorpheusComponent( MarketplaceSettingsPage, ) -function getMarketplaceValueSpec(): CT.ValueSpecObject { +function getMarketplaceValueSpec(): IST.ValueSpecObject { return { type: 'object', name: 'Add Custom Registry', diff --git a/web/projects/ui/src/app/modals/os-welcome/os-welcome.page.html b/web/projects/ui/src/app/modals/os-welcome/os-welcome.page.html index ee938240a..a0f4f46db 100644 --- a/web/projects/ui/src/app/modals/os-welcome/os-welcome.page.html +++ b/web/projects/ui/src/app/modals/os-welcome/os-welcome.page.html @@ -14,7 +14,10 @@

0.3.6-alpha.5

This is an ALPHA release! DO NOT use for production data!
-
Expect that any data you create or store on this version of the OS can be LOST FOREVER!
+
+ Expect that any data you create or store on this version of the OS can be + LOST FOREVER! +
- + +

{{ action.name }}

{{ action.description }}

+

+ {{ disabledText }} +

diff --git a/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.html b/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.html index ec425f8bb..6b72c38a8 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.html +++ b/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.html @@ -8,32 +8,28 @@ - - - - Standard Actions - + + + Standard Actions + - - - Actions for {{ pkg.stateInfo.manifest.title }} - - - - + + + Actions for {{ pkg.manifest.title }} + + + diff --git a/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts b/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts index 99bd70e48..f52cd604d 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts @@ -1,38 +1,15 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { ActivatedRoute } from '@angular/router' -import { AlertController, ModalController, NavController } from '@ionic/angular' -import { - ErrorService, - getPkgId, - isEmptyObject, - LoadingService, -} from '@start9labs/shared' +import { AlertController, NavController } from '@ionic/angular' +import { ErrorService, getPkgId, LoadingService } from '@start9labs/shared' import { T } from '@start9labs/start-sdk' import { PatchDB } from 'patch-db-client' -import { FormComponent } from 'src/app/components/form.component' -import { ActionSuccessPage } from 'src/app/modals/action-success/action-success.page' import { ApiService } from 'src/app/services/api/embassy-api.service' -import { FormDialogService } from 'src/app/services/form-dialog.service' -import { - DataModel, - PackageDataEntry, -} from 'src/app/services/patch-db/data-model' +import { ActionService } from 'src/app/services/action.service' +import { DataModel } from 'src/app/services/patch-db/data-model' import { getAllPackages, getManifest } from 'src/app/util/get-package-data' import { hasCurrentDeps } from 'src/app/util/has-deps' - -const allowedStatuses = { - onlyRunning: new Set(['running']), - onlyStopped: new Set(['stopped']), - any: new Set([ - 'running', - 'stopped', - 'restarting', - 'restoring', - 'stopping', - 'starting', - 'backingUp', - ]), -} +import { filter, map } from 'rxjs' @Component({ selector: 'app-actions', @@ -42,101 +19,49 @@ const allowedStatuses = { }) export class AppActionsPage { readonly pkgId = getPkgId(this.route) - readonly pkg$ = this.patch.watch$('packageData', this.pkgId) + readonly pkg$ = this.patch.watch$('packageData', this.pkgId).pipe( + filter(pkg => pkg.stateInfo.state === 'installed'), + map(pkg => ({ + mainStatus: pkg.status.main, + manifest: getManifest(pkg), + actions: Object.keys(pkg.actions) + .filter(id => id !== 'config') + .map(id => ({ + id, + ...pkg.actions[id], + })), + })), + ) constructor( private readonly route: ActivatedRoute, - private readonly embassyApi: ApiService, - private readonly modalCtrl: ModalController, + private readonly api: ApiService, private readonly alertCtrl: AlertController, private readonly errorService: ErrorService, private readonly loader: LoadingService, private readonly navCtrl: NavController, private readonly patch: PatchDB, - private readonly formDialog: FormDialogService, + private readonly actionService: ActionService, ) {} async handleAction( - status: T.Status, - action: { key: string; value: T.ActionMetadata }, + mainStatus: T.MainStatus['main'], + manifest: T.Manifest, + action: T.ActionMetadata & { id: string }, ) { - if ( - status && - allowedStatuses[action.value.allowedStatuses].has(status.main.status) - ) { - if (!isEmptyObject(action.value.input || {})) { - this.formDialog.open(FormComponent, { - label: action.value.name, - data: { - spec: action.value.input, - buttons: [ - { - text: 'Execute', - handler: async (value: any) => - this.executeAction(action.key, value), - }, - ], - }, - }) - } else { - const alert = await this.alertCtrl.create({ - header: 'Confirm', - message: `Are you sure you want to execute action "${ - action.value.name - }"? ${action.value.warning || ''}`, - buttons: [ - { - text: 'Cancel', - role: 'cancel', - }, - { - text: 'Execute', - handler: () => { - this.executeAction(action.key) - }, - cssClass: 'enter-click', - }, - ], - }) - await alert.present() - } - } else { - const statuses = [...allowedStatuses[action.value.allowedStatuses]] - const last = statuses.pop() - let statusesStr = statuses.join(', ') - let error = '' - if (statuses.length) { - if (statuses.length > 1) { - // oxford comma - statusesStr += ',' - } - statusesStr += ` or ${last}` - } else if (last) { - statusesStr = `${last}` - } else { - error = `There is no status for which this action may be run. This is a bug. Please file an issue with the service maintainer.` - } - const alert = await this.alertCtrl.create({ - header: 'Forbidden', - message: - error || - `Action "${action.value.name}" can only be executed when service is ${statusesStr}`, - buttons: ['OK'], - cssClass: 'alert-error-message enter-click', - }) - await alert.present() - } + this.actionService.present( + { id: manifest.id, title: manifest.title, mainStatus }, + { id: action.id, metadata: action }, + ) } - async tryUninstall(pkg: PackageDataEntry): Promise { - const { title, alerts } = getManifest(pkg) - + async tryUninstall(manifest: T.Manifest): Promise { let message = - alerts.uninstall || - `Uninstalling ${title} will permanently delete its data` + manifest.alerts.uninstall || + `Uninstalling ${manifest.title} will permanently delete its data` if (hasCurrentDeps(this.pkgId, await getAllPackages(this.patch))) { - message = `${message}. Services that depend on ${title} will no longer work properly and may crash` + message = `${message}. Services that depend on ${manifest.title} will no longer work properly and may crash` } const alert = await this.alertCtrl.create({ @@ -165,8 +90,8 @@ export class AppActionsPage { const loader = this.loader.open(`Beginning uninstall...`).subscribe() try { - await this.embassyApi.uninstallPackage({ id: this.pkgId }) - this.embassyApi + await this.api.uninstallPackage({ id: this.pkgId }) + this.api .setDbValue(['ackInstructions', this.pkgId], false) .catch(e => console.error('Failed to mark instructions as unseen', e)) this.navCtrl.navigateRoot('/services') @@ -176,46 +101,6 @@ export class AppActionsPage { loader.unsubscribe() } } - - private async executeAction( - actionId: string, - input?: object, - ): Promise { - const loader = this.loader.open('Executing action...').subscribe() - - try { - const res = await this.embassyApi.executePackageAction({ - id: this.pkgId, - actionId, - input, - }) - - const successModal = await this.modalCtrl.create({ - component: ActionSuccessPage, - componentProps: { - actionRes: res, - }, - }) - - setTimeout(() => successModal.present(), 500) - return true // needed to dismiss original modal/alert - } catch (e: any) { - this.errorService.handleError(e) - return false // don't dismiss original modal/alert - } finally { - loader.unsubscribe() - } - } - - asIsOrder() { - return 0 - } -} - -interface LocalAction { - name: string - description: string - icon: string } @Component({ @@ -225,5 +110,18 @@ interface LocalAction { changeDetection: ChangeDetectionStrategy.OnPush, }) export class AppActionsItemComponent { - @Input() action!: LocalAction + @Input() action!: { + name: string + description: string + visibility: T.ActionVisibility + } + + @Input() icon!: string + + get disabledText() { + return ( + typeof this.action.visibility === 'object' && + this.action.visibility.disabled.reason + ) + } } diff --git a/web/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.html b/web/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.html index 8225d7e53..d4238bfbf 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.html +++ b/web/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.html @@ -26,9 +26,7 @@ fill="clear" color="primary" (click)="launchUi($event, pkg.entry.serviceInterfaces, pkg.entry.hosts)" - [disabled]=" - !(pkg.entry.stateInfo.state | isLaunchable : pkgMainStatus.status) - " + [disabled]="!(pkg.entry.stateInfo.state | isLaunchable : pkgMainStatus)" >
diff --git a/web/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.ts b/web/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.ts index 1464ac8a2..fdc0a3b2a 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.ts @@ -15,16 +15,12 @@ export class AppListPkgComponent { constructor(private readonly launcherService: UiLauncherService) {} - get pkgMainStatus(): T.MainStatus { - return ( - this.pkg.entry.status.main || { - status: 'stopped', - } - ) + get pkgMainStatus(): T.MainStatus['main'] { + return this.pkg.entry.status.main } get sigtermTimeout(): string | null { - return this.pkgMainStatus.status === 'stopping' ? '30s' : null // @dr-bonez TODO + return this.pkgMainStatus === 'stopping' ? '30s' : null // @dr-bonez TODO } launchUi( diff --git a/web/projects/ui/src/app/pages/apps-routes/app-properties/app-properties.page.ts b/web/projects/ui/src/app/pages/apps-routes/app-properties/app-properties.page.ts index af659c32b..521542c10 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-properties/app-properties.page.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-properties/app-properties.page.ts @@ -34,7 +34,7 @@ export class AppPropertiesPage { unmasked: { [key: string]: boolean } = {} stopped$ = this.patch - .watch$('packageData', this.pkgId, 'status', 'main', 'status') + .watch$('packageData', this.pkgId, 'status', 'main') .pipe(map(status => status === 'stopped')) @ViewChild(IonBackButtonDelegate, { static: false }) diff --git a/web/projects/ui/src/app/pages/apps-routes/app-show/app-show.page.html b/web/projects/ui/src/app/pages/apps-routes/app-show/app-show.page.html index 390c6c642..7ea0b86fd 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-show/app-show.page.html +++ b/web/projects/ui/src/app/pages/apps-routes/app-show/app-show.page.html @@ -22,8 +22,8 @@ > , private readonly depErrorService: DepErrorService, - private readonly formDialog: FormDialogService, + private readonly actionService: ActionService, ) {} showProgress( @@ -89,9 +89,9 @@ export class AppShowPage { ): DependencyInfo[] { const manifest = getManifest(pkg) - return Object.keys(pkg.currentDependencies) - .filter(id => !!manifest.dependencies[id]) - .map(id => this.getDepValues(pkg, allPkgs, manifest, id, depErrors)) + return Object.keys(pkg.currentDependencies).map(id => + this.getDepValues(pkg, allPkgs, manifest, id, depErrors), + ) } private getDepDetails( @@ -113,8 +113,8 @@ export class AppShowPage { } } else { return { - title: title ? title : depId, - icon: icon ? icon : 'assets/img/service-icons/fallback.png', + title: title || depId, + icon: icon || 'assets/img/service-icons/fallback.png', versionRange, } } @@ -200,20 +200,34 @@ export class AppShowPage { pkg: PackageDataEntry, pkgManifest: T.Manifest, action: 'install' | 'update' | 'configure', - id: string, - ): Promise { + depId: string, + ) { switch (action) { case 'install': case 'update': - return this.installDep(pkg, pkgManifest, id) + return this.installDep(pkg, pkgManifest, depId) case 'configure': - return this.formDialog.open(ConfigModal, { - label: `${pkgManifest.title} config`, - data: { - pkgId: id, - dependentInfo: pkgManifest, + const depPkg = await getPackage(this.patch, depId) + if (!depPkg) return + + const depManifest = getManifest(depPkg) + return this.actionService.present( + { + id: depId, + title: depManifest.title, + mainStatus: depPkg.status.main, }, - }) + { id: 'config', metadata: pkg.actions['config'] }, + { + title: pkgManifest.title, + request: Object.values(pkg.requestedActions).find( + r => + r.active && + r.request.packageId === depId && + r.request.actionId === 'config', + )!.request, + }, + ) } } diff --git a/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.html b/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.html index c5d18907c..5dc5faf67 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.html +++ b/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.html @@ -36,7 +36,7 @@ diff --git a/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts b/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts index 308db67be..c355bd008 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts @@ -3,10 +3,9 @@ import { AlertController } from '@ionic/angular' import { ErrorService, LoadingService } from '@start9labs/shared' import { T } from '@start9labs/start-sdk' import { PatchDB } from 'patch-db-client' -import { ConfigModal, PackageConfigData } from 'src/app/modals/config.component' +import { ActionService } from 'src/app/services/action.service' import { ApiService } from 'src/app/services/api/embassy-api.service' import { ConnectionService } from 'src/app/services/connection.service' -import { FormDialogService } from 'src/app/services/form-dialog.service' import { DataModel, PackageDataEntry, @@ -20,6 +19,7 @@ import { getAllPackages, getManifest, isInstalled, + needsConfig, } from 'src/app/util/get-package-data' import { hasCurrentDeps } from 'src/app/util/has-deps' @@ -39,6 +39,7 @@ export class AppShowStatusComponent { PR = PrimaryRendering isInstalled = isInstalled + needsConfig = needsConfig constructor( private readonly alertCtrl: AlertController, @@ -46,9 +47,9 @@ export class AppShowStatusComponent { private readonly loader: LoadingService, private readonly embassyApi: ApiService, private readonly launcherService: UiLauncherService, - private readonly formDialog: FormDialogService, readonly connection$: ConnectionService, private readonly patch: PatchDB, + private readonly actionService: ActionService, ) {} get interfaces(): PackageDataEntry['serviceInterfaces'] { @@ -59,7 +60,7 @@ export class AppShowStatusComponent { return this.pkg.hosts } - get pkgStatus(): T.Status { + get pkgStatus(): T.MainStatus { return this.pkg.status } @@ -75,12 +76,15 @@ export class AppShowStatusComponent { return ['running', 'starting', 'restarting'].includes(this.status.primary) } - get isStopped(): boolean { - return this.status.primary === 'stopped' + get canStart(): boolean { + return ( + this.status.primary === 'stopped' && + !Object.keys(this.pkg.requestedActions).length + ) } get sigtermTimeout(): string | null { - return this.pkgStatus?.main.status === 'stopping' ? '30s' : null // @dr-bonez TODO + return this.pkgStatus?.main === 'stopping' ? '30s' : null // @dr-bonez TODO } launchUi( @@ -91,9 +95,14 @@ export class AppShowStatusComponent { } async presentModalConfig(): Promise { - return this.formDialog.open(ConfigModal, { - data: { pkgId: this.manifest.id }, - }) + return this.actionService.present( + { + id: this.manifest.id, + title: this.manifest.title, + mainStatus: this.pkg.status.main, + }, + { id: 'config', metadata: this.pkg.actions['config'] }, + ) } async tryStart(): Promise { diff --git a/web/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-buttons.pipe.ts b/web/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-buttons.pipe.ts index 87bb5423f..c9bb73bfe 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-buttons.pipe.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-buttons.pipe.ts @@ -1,6 +1,6 @@ import { Pipe, PipeTransform } from '@angular/core' import { ActivatedRoute } from '@angular/router' -import { ModalController, NavController } from '@ionic/angular' +import { AlertController, ModalController, NavController } from '@ionic/angular' import { MarkdownComponent } from '@start9labs/shared' import { DataModel, @@ -8,10 +8,10 @@ import { PackageDataEntry, } from 'src/app/services/patch-db/data-model' import { ApiService } from 'src/app/services/api/embassy-api.service' -import { from, map, Observable } from 'rxjs' +import { from, map, Observable, of } from 'rxjs' import { PatchDB } from 'patch-db-client' -import { FormDialogService } from 'src/app/services/form-dialog.service' -import { ConfigModal, PackageConfigData } from 'src/app/modals/config.component' +import { ActionService } from 'src/app/services/action.service' +import { needsConfig } from 'src/app/util/get-package-data' export interface Button { title: string @@ -33,7 +33,8 @@ export class ToButtonsPipe implements PipeTransform { private readonly apiService: ApiService, private readonly api: ApiService, private readonly patch: PatchDB, - private readonly formDialog: FormDialogService, + private readonly actionService: ActionService, + private readonly alertCtrl: AlertController, ) {} transform(pkg: PackageDataEntry): Button[] { @@ -53,13 +54,29 @@ export class ToButtonsPipe implements PipeTransform { // config { action: async () => - this.formDialog.open(ConfigModal, { - label: `${manifest.title} configuration`, - data: { pkgId: manifest.id }, - }), + pkg.actions['config'] + ? this.actionService.present( + { + id: manifest.id, + title: manifest.title, + mainStatus: pkg.status.main, + }, + { + id: 'config', + metadata: pkg.actions['config'], + }, + ) + : this.alertCtrl + .create({ + header: 'No Config', + message: `No config options for ${manifest.title} v${manifest.version}`, + buttons: ['OK'], + }) + .then(a => a.present()), title: 'Config', description: `Customize ${manifest.title}`, icon: 'options-outline', + highlighted$: of(needsConfig(manifest.id, pkg.requestedActions)), }, // properties { diff --git a/web/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-health-checks.pipe.ts b/web/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-health-checks.pipe.ts index 24153caf9..12d731010 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-health-checks.pipe.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-health-checks.pipe.ts @@ -15,10 +15,10 @@ export class ToHealthChecksPipe implements PipeTransform { transform( manifest: T.Manifest, ): Observable | null> { - return this.patch.watch$('packageData', manifest.id, 'status', 'main').pipe( - map(main => { - return main.status === 'running' && !isEmptyObject(main.health) - ? main.health + return this.patch.watch$('packageData', manifest.id, 'status').pipe( + map(status => { + return status.main === 'running' && !isEmptyObject(status.health) + ? status.health : null }), startWith(null), diff --git a/web/projects/ui/src/app/pages/server-routes/server-backup/backing-up/backing-up.component.ts b/web/projects/ui/src/app/pages/server-routes/server-backup/backing-up/backing-up.component.ts index d88ab8838..7c7f381a1 100644 --- a/web/projects/ui/src/app/pages/server-routes/server-backup/backing-up/backing-up.component.ts +++ b/web/projects/ui/src/app/pages/server-routes/server-backup/backing-up/backing-up.component.ts @@ -30,8 +30,8 @@ export class BackingUpComponent { name: 'pkgMainStatus', }) export class PkgMainStatusPipe implements PipeTransform { - transform(pkgId: string): Observable { - return this.patch.watch$('packageData', pkgId, 'status', 'main', 'status') + transform(pkgId: string): Observable { + return this.patch.watch$('packageData', pkgId, 'status', 'main') } constructor(private readonly patch: PatchDB) {} diff --git a/web/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts b/web/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts index 8ddc1c8ca..12ecd4e61 100644 --- a/web/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts +++ b/web/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts @@ -9,7 +9,7 @@ import { import { WINDOW } from '@ng-web-apis/common' import * as argon2 from '@start9labs/argon2' import { ErrorService, LoadingService } from '@start9labs/shared' -import { CB } from '@start9labs/start-sdk' +import { ISB } from '@start9labs/start-sdk' import { TuiAlertService, TuiDialogService } from '@taiga-ui/core' import { TUI_PROMPT } from '@taiga-ui/kit' import { PatchDB } from 'patch-db-client' @@ -694,22 +694,22 @@ interface SettingBtn { disabled$: Observable } -const passwordSpec = CB.Config.of({ - currentPassword: CB.Value.text({ +const passwordSpec = ISB.InputSpec.of({ + currentPassword: ISB.Value.text({ name: 'Current Password', required: { default: null, }, masked: true, }), - newPassword1: CB.Value.text({ + newPassword1: ISB.Value.text({ name: 'New Password', required: { default: null, }, masked: true, }), - newPassword2: CB.Value.text({ + newPassword2: ISB.Value.text({ name: 'Retype New Password', required: { default: null, diff --git a/web/projects/ui/src/app/pages/server-routes/wifi/wifi.page.ts b/web/projects/ui/src/app/pages/server-routes/wifi/wifi.page.ts index 5d722b482..7d59a4d30 100644 --- a/web/projects/ui/src/app/pages/server-routes/wifi/wifi.page.ts +++ b/web/projects/ui/src/app/pages/server-routes/wifi/wifi.page.ts @@ -7,7 +7,7 @@ import { import { ActionSheetButton, AlertInput } from '@ionic/core' import { WINDOW } from '@ng-web-apis/common' import { ErrorService, LoadingService, pauseFor } from '@start9labs/shared' -import { CT } from '@start9labs/start-sdk' +import { IST } from '@start9labs/start-sdk' import { TuiDialogOptions } from '@taiga-ui/core' import { PatchDB } from 'patch-db-client' import { FormComponent, FormContext } from 'src/app/components/form.component' @@ -343,7 +343,7 @@ export class WifiPage { function getWifiValueSpec( ssid: string | null = null, needsPW: boolean = true, -): CT.ValueSpecObject { +): IST.ValueSpecObject { return { warning: null, type: 'object', diff --git a/web/projects/ui/src/app/pipes/launchable/launchable.pipe.ts b/web/projects/ui/src/app/pipes/launchable/launchable.pipe.ts index 9077c5215..405d61019 100644 --- a/web/projects/ui/src/app/pipes/launchable/launchable.pipe.ts +++ b/web/projects/ui/src/app/pipes/launchable/launchable.pipe.ts @@ -10,7 +10,7 @@ export class LaunchablePipe implements PipeTransform { transform( state: T.PackageState['state'], - status: T.MainStatus['status'], + status: T.MainStatus['main'], ): boolean { return this.configService.isLaunchable(state, status) } diff --git a/web/projects/ui/src/app/services/action.service.ts b/web/projects/ui/src/app/services/action.service.ts new file mode 100644 index 000000000..74df5ffc6 --- /dev/null +++ b/web/projects/ui/src/app/services/action.service.ts @@ -0,0 +1,159 @@ +import { Injectable } from '@angular/core' +import { AlertController, ModalController } from '@ionic/angular' +import { ErrorService, LoadingService } from '@start9labs/shared' +import { T } from '@start9labs/start-sdk' +import { ActionSuccessPage } from 'src/app/modals/action-success/action-success.page' +import { RR } from 'src/app/services/api/api.types' +import { ApiService } from 'src/app/services/api/embassy-api.service' +import { FormDialogService } from 'src/app/services/form-dialog.service' +import { + ActionInputModal, + PackageActionData, +} from '../modals/action-input.component' + +const allowedStatuses = { + 'only-running': new Set(['running']), + 'only-stopped': new Set(['stopped']), + any: new Set([ + 'running', + 'stopped', + 'restarting', + 'restoring', + 'stopping', + 'starting', + 'backingUp', + ]), +} + +@Injectable({ + providedIn: 'root', +}) +export class ActionService { + constructor( + private readonly api: ApiService, + private readonly modalCtrl: ModalController, + private readonly alertCtrl: AlertController, + private readonly errorService: ErrorService, + private readonly loader: LoadingService, + private readonly formDialog: FormDialogService, + ) {} + + async present( + pkgInfo: { + id: string + title: string + mainStatus: T.MainStatus['main'] + }, + actionInfo: { + id: string + metadata: T.ActionMetadata + }, + dependentInfo?: { + title: string + request: T.ActionRequest + }, + ) { + if ( + allowedStatuses[actionInfo.metadata.allowedStatuses].has( + pkgInfo.mainStatus, + ) + ) { + if (actionInfo.metadata.hasInput) { + this.formDialog.open(ActionInputModal, { + label: actionInfo.metadata.name, + data: { + pkgInfo, + actionInfo: { + id: actionInfo.id, + warning: actionInfo.metadata.warning, + }, + dependentInfo, + }, + }) + } else { + const alert = await this.alertCtrl.create({ + header: 'Confirm', + message: `Are you sure you want to execute action "${ + actionInfo.metadata.name + }"? ${actionInfo.metadata.warning || ''}`, + buttons: [ + { + text: 'Cancel', + role: 'cancel', + }, + { + text: 'Execute', + handler: () => { + this.execute(pkgInfo.id, actionInfo.id) + }, + cssClass: 'enter-click', + }, + ], + }) + await alert.present() + } + } else { + const statuses = [...allowedStatuses[actionInfo.metadata.allowedStatuses]] + const last = statuses.pop() + let statusesStr = statuses.join(', ') + let error = '' + if (statuses.length) { + if (statuses.length > 1) { + // oxford comma + statusesStr += ',' + } + statusesStr += ` or ${last}` + } else if (last) { + statusesStr = `${last}` + } else { + error = `There is no status for which this action may be run. This is a bug. Please file an issue with the service maintainer.` + } + const alert = await this.alertCtrl.create({ + header: 'Forbidden', + message: + error || + `Action "${actionInfo.metadata.name}" can only be executed when service is ${statusesStr}`, + buttons: ['OK'], + cssClass: 'alert-error-message enter-click', + }) + await alert.present() + } + } + + async execute( + packageId: string, + actionId: string, + inputs?: { + prev: RR.GetActionInputRes + curr: object + }, + ): Promise { + const loader = this.loader.open('Executing action...').subscribe() + + try { + const res = await this.api.runAction({ + packageId, + actionId, + prev: inputs?.prev || null, + input: inputs?.curr || null, + }) + + if (res) { + const successModal = await this.modalCtrl.create({ + component: ActionSuccessPage, + componentProps: { + actionRes: res, + }, + }) + + setTimeout(() => successModal.present(), 500) + } + return true // needed to dismiss original modal/alert + } catch (e: any) { + this.errorService.handleError(e) + return false // don't dismiss original modal/alert + } finally { + loader.unsubscribe() + } + } +} diff --git a/web/projects/ui/src/app/services/api/api.fixures.ts b/web/projects/ui/src/app/services/api/api.fixures.ts index b093ad29e..e3aa63ee3 100644 --- a/web/projects/ui/src/app/services/api/api.fixures.ts +++ b/web/projects/ui/src/app/services/api/api.fixures.ts @@ -6,7 +6,7 @@ import { Metric, NotificationLevel, RR, ServerNotifications } from './api.types' import { BTC_ICON, LND_ICON, PROXY_ICON, REGISTRY_ICON } from './api-icons' import { Log } from '@start9labs/shared' import { configBuilderToSpec } from 'src/app/util/configBuilderToSpec' -import { T, CB } from '@start9labs/start-sdk' +import { T, ISB, IST } from '@start9labs/start-sdk' import { GetPackagesRes } from '@start9labs/marketplace' const mockBlake3Commitment: T.Blake3Commitment = { @@ -112,7 +112,6 @@ export module Mock { }, osVersion: '0.2.12', dependencies: {}, - hasConfig: true, images: { main: { source: 'packed', @@ -170,7 +169,6 @@ export module Mock { s9pk: '', }, }, - hasConfig: true, images: { main: { source: 'packed', @@ -221,7 +219,6 @@ export module Mock { s9pk: '', }, }, - hasConfig: false, images: { main: { source: 'packed', @@ -949,7 +946,8 @@ export module Mock { }, } - export const ActionResponse: RR.ExecutePackageActionRes = { + export const ActionResponse: T.ActionResult = { + version: '0', message: 'Password changed successfully. If you lose your new password, you will be lost forever.', value: 'NewPassword1234!', @@ -1137,31 +1135,29 @@ export module Mock { }, } - export const getInputSpec = async (): Promise< - RR.GetPackageConfigRes['spec'] - > => + export const getActionInputSpec = async (): Promise => configBuilderToSpec( - CB.Config.of({ - bitcoin: CB.Value.object( + ISB.InputSpec.of({ + bitcoin: ISB.Value.object( { name: 'Bitcoin Settings', description: 'RPC and P2P interface configuration options for Bitcoin Core', }, - CB.Config.of({ - 'bitcoind-p2p': CB.Value.union( + ISB.InputSpec.of({ + 'bitcoind-p2p': ISB.Value.union( { name: 'P2P Settings', description: '

The Bitcoin Core node to connect to over the peer-to-peer (P2P) interface:

  • Bitcoin Core: The Bitcoin Core service installed on this device
  • External Node: A Bitcoin node running on a different device
', required: { default: 'internal' }, }, - CB.Variants.of({ - internal: { name: 'Bitcoin Core', spec: CB.Config.of({}) }, + ISB.Variants.of({ + internal: { name: 'Bitcoin Core', spec: ISB.InputSpec.of({}) }, external: { name: 'External Node', - spec: CB.Config.of({ - 'p2p-host': CB.Value.text({ + spec: ISB.InputSpec.of({ + 'p2p-host': ISB.Value.text({ name: 'Public Address', required: { default: null, @@ -1169,7 +1165,7 @@ export module Mock { description: 'The public address of your Bitcoin Core server', }), - 'p2p-port': CB.Value.number({ + 'p2p-port': ISB.Value.number({ name: 'P2P Port', description: 'The port that your Bitcoin Core P2P server is bound to', @@ -1186,24 +1182,23 @@ export module Mock { ), }), ), - color: CB.Value.color({ + color: ISB.Value.color({ name: 'Color', required: false, }), - datetime: CB.Value.datetime({ + datetime: ISB.Value.datetime({ name: 'Datetime', required: false, }), - file: CB.Value.file({ + file: ISB.Value.file({ name: 'File', required: false, extensions: ['png', 'pdf'], }), - users: CB.Value.multiselect({ + users: ISB.Value.multiselect({ name: 'Users', default: [], maxLength: 2, - disabled: ['matt'], values: { matt: 'Matt Hill', alex: 'Alex Inkin', @@ -1211,21 +1206,19 @@ export module Mock { lucy: 'Lucy', }, }), - advanced: CB.Value.object( + advanced: ISB.Value.object( { name: 'Advanced', description: 'Advanced settings', }, - CB.Config.of({ - rpcsettings: CB.Value.object( + ISB.InputSpec.of({ + rpcsettings: ISB.Value.object( { name: 'RPC Settings', description: 'rpc username and password', - warning: - 'Adding RPC users gives them special permissions on your node.', }, - CB.Config.of({ - rpcuser2: CB.Value.text({ + ISB.InputSpec.of({ + rpcuser2: ISB.Value.text({ name: 'RPC Username', required: { default: 'defaultrpcusername', @@ -1238,7 +1231,7 @@ export module Mock { }, ], }), - rpcuser: CB.Value.text({ + rpcuser: ISB.Value.text({ name: 'RPC Username', required: { default: 'defaultrpcusername', @@ -1251,7 +1244,7 @@ export module Mock { }, ], }), - rpcpass: CB.Value.text({ + rpcpass: ISB.Value.text({ name: 'RPC User Password', required: { default: { @@ -1261,7 +1254,7 @@ export module Mock { }, description: 'rpc password', }), - rpcpass2: CB.Value.text({ + rpcpass2: ISB.Value.text({ name: 'RPC User Password', required: { default: { @@ -1275,15 +1268,15 @@ export module Mock { ), }), ), - testnet: CB.Value.toggle({ + testnet: ISB.Value.toggle({ name: 'Testnet', default: true, description: '
  • determines whether your node is running on testnet or mainnet
', warning: 'Chain will have to resync!', }), - 'object-list': CB.Value.list( - CB.List.obj( + 'object-list': ISB.Value.list( + ISB.List.obj( { name: 'Object List', minLength: 0, @@ -1295,13 +1288,13 @@ export module Mock { description: 'This is a list of objects, like users or something', }, { - spec: CB.Config.of({ - 'first-name': CB.Value.text({ + spec: ISB.InputSpec.of({ + 'first-name': ISB.Value.text({ name: 'First Name', required: false, description: 'User first name', }), - 'last-name': CB.Value.text({ + 'last-name': ISB.Value.text({ name: 'Last Name', required: { default: { @@ -1317,7 +1310,7 @@ export module Mock { }, ], }), - age: CB.Value.number({ + age: ISB.Value.number({ name: 'Age', description: 'The age of the user', warning: 'User must be at least 18.', @@ -1331,8 +1324,8 @@ export module Mock { }, ), ), - 'union-list': CB.Value.list( - CB.List.obj( + 'union-list': ISB.Value.list( + ISB.List.obj( { name: 'Union List', minLength: 0, @@ -1342,27 +1335,27 @@ export module Mock { warning: 'If you change this, things may work.', }, { - spec: CB.Config.of({ + spec: ISB.InputSpec.of({ /* TODO: Convert range for this value ([0, 2])*/ - union: CB.Value.union( + union: ISB.Value.union( { name: 'Preference', description: null, warning: null, required: { default: 'summer' }, }, - CB.Variants.of({ + ISB.Variants.of({ summer: { name: 'summer', - spec: CB.Config.of({ - 'favorite-tree': CB.Value.text({ + spec: ISB.InputSpec.of({ + 'favorite-tree': ISB.Value.text({ name: 'Favorite Tree', required: { default: 'Maple', }, description: 'What is your favorite tree?', }), - 'favorite-flower': CB.Value.select({ + 'favorite-flower': ISB.Value.select({ name: 'Favorite Flower', description: 'Select your favorite flower', required: { @@ -1379,8 +1372,8 @@ export module Mock { }, winter: { name: 'winter', - spec: CB.Config.of({ - 'like-snow': CB.Value.toggle({ + spec: ISB.InputSpec.of({ + 'like-snow': ISB.Value.toggle({ name: 'Like Snow?', default: true, description: 'Do you like snow or not?', @@ -1394,7 +1387,7 @@ export module Mock { }, ), ), - 'random-select': CB.Value.select({ + 'random-select': ISB.Value.dynamicSelect(() => ({ name: 'Random select', description: 'This is not even real.', warning: 'Be careful changing this!', @@ -1407,47 +1400,47 @@ export module Mock { option3: 'option3', }, disabled: ['option2'], - }), + })), 'favorite-number': - /* TODO: Convert range for this value ((-100,100])*/ CB.Value.number({ - name: 'Favorite Number', - description: 'Your favorite number of all time', - warning: - 'Once you set this number, it can never be changed without severe consequences.', - required: { - default: 7, + /* TODO: Convert range for this value ((-100,100])*/ ISB.Value.number( + { + name: 'Favorite Number', + description: 'Your favorite number of all time', + warning: + 'Once you set this number, it can never be changed without severe consequences.', + required: { + default: 7, + }, + integer: false, + units: 'BTC', }, - integer: false, - units: 'BTC', - }), - rpcsettings: CB.Value.object( + ), + rpcsettings: ISB.Value.object( { name: 'RPC Settings', description: 'rpc username and password', - warning: - 'Adding RPC users gives them special permissions on your node.', }, - CB.Config.of({ - laws: CB.Value.object( + ISB.InputSpec.of({ + laws: ISB.Value.object( { name: 'Laws', description: 'the law of the realm', }, - CB.Config.of({ - law1: CB.Value.text({ + ISB.InputSpec.of({ + law1: ISB.Value.text({ name: 'First Law', required: false, description: 'the first law', }), - law2: CB.Value.text({ + law2: ISB.Value.text({ name: 'Second Law', required: false, description: 'the second law', }), }), ), - rulemakers: CB.Value.list( - CB.List.obj( + rulemakers: ISB.Value.list( + ISB.List.obj( { name: 'Rule Makers', minLength: 0, @@ -1455,8 +1448,8 @@ export module Mock { description: 'the people who make the rules', }, { - spec: CB.Config.of({ - rulemakername: CB.Value.text({ + spec: ISB.InputSpec.of({ + rulemakername: ISB.Value.text({ name: 'Rulemaker Name', required: { default: { @@ -1466,7 +1459,7 @@ export module Mock { }, description: 'the name of the rule maker', }), - rulemakerip: CB.Value.text({ + rulemakerip: ISB.Value.text({ name: 'Rulemaker IP', required: { default: '192.168.1.0', @@ -1484,7 +1477,7 @@ export module Mock { }, ), ), - rpcuser: CB.Value.text({ + rpcuser: ISB.Value.text({ name: 'RPC Username', required: { default: 'defaultrpcusername', @@ -1497,7 +1490,7 @@ export module Mock { }, ], }), - rpcpass: CB.Value.text({ + rpcpass: ISB.Value.text({ name: 'RPC User Password', required: { default: { @@ -1510,33 +1503,32 @@ export module Mock { }), }), ), - 'bitcoin-node': CB.Value.union( + 'bitcoin-node': ISB.Value.union( { name: 'Bitcoin Node', description: 'Options
  • Item 1
  • Item 2
', warning: 'Careful changing this', required: { default: 'internal' }, - disabled: ['fake'], }, - CB.Variants.of({ + ISB.Variants.of({ fake: { name: 'Fake', - spec: CB.Config.of({}), + spec: ISB.InputSpec.of({}), }, internal: { name: 'Internal', - spec: CB.Config.of({}), + spec: ISB.InputSpec.of({}), }, external: { name: 'External', - spec: CB.Config.of({ - 'emergency-contact': CB.Value.object( + spec: ISB.InputSpec.of({ + 'emergency-contact': ISB.Value.object( { name: 'Emergency Contact', description: 'The person to contact in case of emergency.', }, - CB.Config.of({ - name: CB.Value.text({ + ISB.InputSpec.of({ + name: ISB.Value.text({ name: 'Name', required: { default: null, @@ -1548,7 +1540,7 @@ export module Mock { }, ], }), - email: CB.Value.text({ + email: ISB.Value.text({ name: 'Email', inputmode: 'email', required: { @@ -1557,7 +1549,7 @@ export module Mock { }), }), ), - 'public-domain': CB.Value.text({ + 'public-domain': ISB.Value.text({ name: 'Public Domain', required: { default: 'bitcoinnode.com', @@ -1570,7 +1562,7 @@ export module Mock { }, ], }), - 'private-domain': CB.Value.text({ + 'private-domain': ISB.Value.text({ name: 'Private Domain', required: { default: null, @@ -1583,7 +1575,7 @@ export module Mock { }, }), ), - port: CB.Value.number({ + port: ISB.Value.number({ name: 'Port', description: 'the default port for your Bitcoin node. default: 8333, testnet: 18333, regtest: 18444', @@ -1595,7 +1587,7 @@ export module Mock { step: 1, integer: true, }), - 'favorite-slogan': CB.Value.text({ + 'favorite-slogan': ISB.Value.text({ name: 'Favorite Slogan', generate: { charset: 'a-z,A-Z,2-9', @@ -1606,8 +1598,8 @@ export module Mock { 'You most favorite slogan in the whole world, used for paying you.', masked: true, }), - rpcallowip: CB.Value.list( - CB.List.text( + rpcallowip: ISB.Value.list( + ISB.List.text( { name: 'RPC Allowed IPs', minLength: 1, @@ -1629,8 +1621,8 @@ export module Mock { }, ), ), - rpcauth: CB.Value.list( - CB.List.text( + rpcauth: ISB.Value.list( + ISB.List.text( { name: 'RPC Auth', description: @@ -1694,14 +1686,21 @@ export module Mock { icon: '/assets/img/service-icons/bitcoind.svg', lastBackup: null, status: { - configured: true, - main: { - status: 'running', - started: new Date().toISOString(), - health: {}, + main: 'running', + started: new Date().toISOString(), + health: {}, + }, + actions: { + config: { + name: 'Bitcoin Config', + description: 'edit bitcoin.conf', + warning: null, + visibility: 'enabled', + allowedStatuses: 'any', + hasInput: true, + group: null, }, }, - actions: {}, serviceInterfaces: { ui: { id: 'ui', @@ -1860,6 +1859,7 @@ export module Mock { storeExposedDependents: [], registry: 'https://registry.start9.com/', developerKey: 'developer-key', + requestedActions: {}, } export const bitcoinProxy: PackageDataEntry = { @@ -1871,10 +1871,7 @@ export module Mock { icon: '/assets/img/service-icons/btc-rpc-proxy.png', lastBackup: null, status: { - configured: false, - main: { - status: 'stopped', - }, + main: 'stopped', }, actions: {}, serviceInterfaces: { @@ -1902,13 +1899,13 @@ export module Mock { kind: 'running', versionRange: '>=26.0.0', healthChecks: [], - configSatisfied: true, }, }, hosts: {}, storeExposedDependents: [], registry: 'https://registry.start9.com/', developerKey: 'developer-key', + requestedActions: {}, } export const lnd: PackageDataEntry = { @@ -1920,10 +1917,7 @@ export module Mock { icon: '/assets/img/service-icons/lnd.png', lastBackup: null, status: { - configured: true, - main: { - status: 'stopped', - }, + main: 'stopped', }, actions: {}, serviceInterfaces: { @@ -1986,20 +1980,19 @@ export module Mock { kind: 'running', versionRange: '>=26.0.0', healthChecks: [], - configSatisfied: true, }, 'btc-rpc-proxy': { title: Mock.MockManifestBitcoinProxy.title, icon: 'assets/img/service-icons/btc-rpc-proxy.png', kind: 'exists', versionRange: '>2.0.0', - configSatisfied: false, }, }, hosts: {}, storeExposedDependents: [], registry: 'https://registry.start9.com/', developerKey: 'developer-key', + requestedActions: {}, } export const LocalPkgs: { [key: string]: PackageDataEntry } = diff --git a/web/projects/ui/src/app/services/api/api.types.ts b/web/projects/ui/src/app/services/api/api.types.ts index ef0e80d20..e428456f6 100644 --- a/web/projects/ui/src/app/services/api/api.types.ts +++ b/web/projects/ui/src/app/services/api/api.types.ts @@ -2,7 +2,7 @@ import { Dump } from 'patch-db-client' import { PackagePropertiesVersioned } from 'src/app/util/properties.util' import { DataModel } from 'src/app/services/patch-db/data-model' import { StartOSDiskInfo, LogsRes, ServerLogsReq } from '@start9labs/shared' -import { CT, T } from '@start9labs/start-sdk' +import { IST, T } from '@start9labs/start-sdk' import { WebSocketSubjectConfig } from 'rxjs/webSocket' export module RR { @@ -72,11 +72,10 @@ export module RR { export type GetServerLogsReq = ServerLogsReq // server.logs & server.kernel-logs export type GetServerLogsRes = LogsRes - // @param limit: BE default is 50 - // @param boot: number is offset (0: current, -1 prev, +1 first), string is a specific boot id, and null is all export type FollowServerLogsReq = { - limit?: number - boot?: number | string | null + limit?: number // (optional) default is 50. Ignored if cursor provided + boot?: number | string | null // (optional) number is offset (0: current, -1 prev, +1 first), string is a specific boot id, null is all. Default is undefined + cursor?: string // the last known log. Websocket will return all logs since this log } // server.logs.follow & server.kernel-logs.follow export type FollowServerLogsRes = { startCursor: string @@ -226,14 +225,19 @@ export module RR { export type InstallPackageReq = T.InstallParams export type InstallPackageRes = null - export type GetPackageConfigReq = { id: string } // package.config.get - export type GetPackageConfigRes = { spec: CT.InputSpec; config: object } + export type GetActionInputReq = { packageId: string; actionId: string } // package.action.get-input + export type GetActionInputRes = { + spec: IST.InputSpec + value: object | null + } - export type DrySetPackageConfigReq = { id: string; config: object } // package.config.set.dry - export type DrySetPackageConfigRes = T.PackageId[] - - export type SetPackageConfigReq = DrySetPackageConfigReq // package.config.set - export type SetPackageConfigRes = null + export type RunActionReq = { + packageId: string + actionId: string + prev: GetActionInputRes | null + input: object | null + } // package.action.run + export type RunActionRes = T.ActionResult | null export type RestorePackagesReq = { // package.backup.restore @@ -244,13 +248,6 @@ export module RR { } export type RestorePackagesRes = null - export type ExecutePackageActionReq = { - id: string - actionId: string - input?: object - } // package.action - export type ExecutePackageActionRes = ActionResponse - export type StartPackageReq = { id: string } // package.start export type StartPackageRes = null @@ -263,16 +260,6 @@ export module RR { export type UninstallPackageReq = { id: string } // package.uninstall export type UninstallPackageRes = null - export type DryConfigureDependencyReq = { - dependencyId: string - dependentId: string - } // package.dependency.configure.dry - export type DryConfigureDependencyRes = { - oldConfig: object - newConfig: object - spec: CT.InputSpec - } - export type SideloadPackageReq = { manifest: T.Manifest icon: string // base64 @@ -306,13 +293,6 @@ export interface TaggedDependencyError { error: DependencyError } -export interface ActionResponse { - message: string - value: string | null - copyable: boolean - qr: boolean -} - interface MetricData { value: string unit: string diff --git a/web/projects/ui/src/app/services/api/embassy-api.service.ts b/web/projects/ui/src/app/services/api/embassy-api.service.ts index 31e3ac86e..1a5c1c57c 100644 --- a/web/projects/ui/src/app/services/api/embassy-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-api.service.ts @@ -231,26 +231,16 @@ export abstract class ApiService { params: RR.InstallPackageReq, ): Promise - abstract getPackageConfig( - params: RR.GetPackageConfigReq, - ): Promise + abstract getActionInput( + params: RR.GetActionInputReq, + ): Promise - abstract drySetPackageConfig( - params: RR.DrySetPackageConfigReq, - ): Promise - - abstract setPackageConfig( - params: RR.SetPackageConfigReq, - ): Promise + abstract runAction(params: RR.RunActionReq): Promise abstract restorePackages( params: RR.RestorePackagesReq, ): Promise - abstract executePackageAction( - params: RR.ExecutePackageActionReq, - ): Promise - abstract startPackage(params: RR.StartPackageReq): Promise abstract restartPackage( @@ -263,9 +253,5 @@ export abstract class ApiService { params: RR.UninstallPackageReq, ): Promise - abstract dryConfigureDependency( - params: RR.DryConfigureDependencyReq, - ): Promise - abstract sideloadPackage(): Promise } diff --git a/web/projects/ui/src/app/services/api/embassy-live-api.service.ts b/web/projects/ui/src/app/services/api/embassy-live-api.service.ts index 48cf6edd3..742076ec8 100644 --- a/web/projects/ui/src/app/services/api/embassy-live-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-live-api.service.ts @@ -468,22 +468,14 @@ export class LiveApiService extends ApiService { return this.rpcRequest({ method: 'package.install', params }) } - async getPackageConfig( - params: RR.GetPackageConfigReq, - ): Promise { - return this.rpcRequest({ method: 'package.config.get', params }) + async getActionInput( + params: RR.GetActionInputReq, + ): Promise { + return this.rpcRequest({ method: 'package.action.get-input', params }) } - async drySetPackageConfig( - params: RR.DrySetPackageConfigReq, - ): Promise { - return this.rpcRequest({ method: 'package.config.set.dry', params }) - } - - async setPackageConfig( - params: RR.SetPackageConfigReq, - ): Promise { - return this.rpcRequest({ method: 'package.config.set', params }) + async runAction(params: RR.RunActionReq): Promise { + return this.rpcRequest({ method: 'package.action.run', params }) } async restorePackages( @@ -492,12 +484,6 @@ export class LiveApiService extends ApiService { return this.rpcRequest({ method: 'package.backup.restore', params }) } - async executePackageAction( - params: RR.ExecutePackageActionReq, - ): Promise { - return this.rpcRequest({ method: 'package.action', params }) - } - async startPackage(params: RR.StartPackageReq): Promise { return this.rpcRequest({ method: 'package.start', params }) } @@ -518,15 +504,6 @@ export class LiveApiService extends ApiService { return this.rpcRequest({ method: 'package.uninstall', params }) } - async dryConfigureDependency( - params: RR.DryConfigureDependencyReq, - ): Promise { - return this.rpcRequest({ - method: 'package.dependency.configure.dry', - params, - }) - } - async sideloadPackage(): Promise { return this.rpcRequest({ method: 'package.sideload', diff --git a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts index a40f29a0a..6c55bb9db 100644 --- a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts @@ -780,37 +780,19 @@ export class MockApiService extends ApiService { return null } - async getPackageConfig( - params: RR.GetPackageConfigReq, - ): Promise { + async getActionInput( + params: RR.GetActionInputReq, + ): Promise { await pauseFor(2000) return { - config: Mock.MockConfig, - spec: await Mock.getInputSpec(), + value: Mock.MockConfig, + spec: await Mock.getActionInputSpec(), } } - async drySetPackageConfig( - params: RR.DrySetPackageConfigReq, - ): Promise { + async runAction(params: RR.RunActionReq): Promise { await pauseFor(2000) - return [] - } - - async setPackageConfig( - params: RR.SetPackageConfigReq, - ): Promise { - await pauseFor(2000) - const patch = [ - { - op: PatchOp.REPLACE, - path: `/packageData/${params.id}/status/configured`, - value: true, - }, - ] - this.mockRevision(patch) - - return null + return Mock.ActionResponse } async restorePackages( @@ -843,13 +825,6 @@ export class MockApiService extends ApiService { return null } - async executePackageAction( - params: RR.ExecutePackageActionReq, - ): Promise { - await pauseFor(2000) - return Mock.ActionResponse - } - async startPackage(params: RR.StartPackageReq): Promise { const path = `/packageData/${params.id}/status/main` @@ -1069,17 +1044,6 @@ export class MockApiService extends ApiService { return null } - async dryConfigureDependency( - params: RR.DryConfigureDependencyReq, - ): Promise { - await pauseFor(2000) - return { - oldConfig: Mock.MockConfig, - newConfig: Mock.MockDependencyConfig, - spec: await Mock.getInputSpec(), - } - } - async sideloadPackage(): Promise { await pauseFor(2000) return { diff --git a/web/projects/ui/src/app/services/api/mock-patch.ts b/web/projects/ui/src/app/services/api/mock-patch.ts index 34804fbc6..cdc8f7173 100644 --- a/web/projects/ui/src/app/services/api/mock-patch.ts +++ b/web/projects/ui/src/app/services/api/mock-patch.ts @@ -96,40 +96,47 @@ export const mockPatchData: DataModel = { icon: '/assets/img/service-icons/bitcoind.svg', lastBackup: null, status: { - configured: true, - main: { - status: 'running', - started: '2021-06-14T20:49:17.774Z', - health: { - 'ephemeral-health-check': { - name: 'Ephemeral Health Check', - result: 'starting', - message: null, - }, - 'chain-state': { - name: 'Chain State', - result: 'loading', - message: 'Bitcoin is syncing from genesis', - }, - 'p2p-interface': { - name: 'P2P', - result: 'success', - message: 'Health check successful', - }, - 'rpc-interface': { - name: 'RPC', - result: 'failure', - message: 'RPC interface unreachable.', - }, - 'unnecessary-health-check': { - name: 'Unnecessary Health Check', - result: 'disabled', - message: null, - }, + main: 'running', + started: '2021-06-14T20:49:17.774Z', + health: { + 'ephemeral-health-check': { + name: 'Ephemeral Health Check', + result: 'starting', + message: null, + }, + 'chain-state': { + name: 'Chain State', + result: 'loading', + message: 'Bitcoin is syncing from genesis', + }, + 'p2p-interface': { + name: 'P2P', + result: 'success', + message: 'Health check successful', + }, + 'rpc-interface': { + name: 'RPC', + result: 'failure', + message: 'RPC interface unreachable.', + }, + 'unnecessary-health-check': { + name: 'Unnecessary Health Check', + result: 'disabled', + message: null, }, }, }, - actions: {}, + actions: { + config: { + name: 'Bitcoin Config', + description: 'edit bitcoin.conf', + warning: null, + visibility: 'enabled', + allowedStatuses: 'any', + hasInput: true, + group: null, + }, + }, serviceInterfaces: { ui: { id: 'ui', @@ -288,6 +295,7 @@ export const mockPatchData: DataModel = { storeExposedDependents: [], registry: 'https://registry.start9.com/', developerKey: 'developer-key', + requestedActions: {}, }, lnd: { stateInfo: { @@ -301,10 +309,7 @@ export const mockPatchData: DataModel = { icon: '/assets/img/service-icons/lnd.png', lastBackup: null, status: { - configured: true, - main: { - status: 'stopped', - }, + main: 'stopped', }, actions: {}, serviceInterfaces: { @@ -367,7 +372,6 @@ export const mockPatchData: DataModel = { kind: 'running', versionRange: '>=26.0.0', healthChecks: [], - configSatisfied: true, }, 'btc-rpc-proxy': { title: 'Bitcoin Proxy', @@ -375,13 +379,13 @@ export const mockPatchData: DataModel = { kind: 'running', versionRange: '>2.0.0', healthChecks: [], - configSatisfied: false, }, }, hosts: {}, storeExposedDependents: [], registry: 'https://registry.start9.com/', developerKey: 'developer-key', + requestedActions: {}, }, }, } diff --git a/web/projects/ui/src/app/services/config.service.ts b/web/projects/ui/src/app/services/config.service.ts index 69fafe815..c6d089dbd 100644 --- a/web/projects/ui/src/app/services/config.service.ts +++ b/web/projects/ui/src/app/services/config.service.ts @@ -51,7 +51,7 @@ export class ConfigService { isLaunchable( state: T.PackageState['state'], - status: T.MainStatus['status'], + status: T.MainStatus['main'], ): boolean { return state === 'installed' && status === 'running' } diff --git a/web/projects/ui/src/app/services/dep-error.service.ts b/web/projects/ui/src/app/services/dep-error.service.ts index a46a5123e..c4de94609 100644 --- a/web/projects/ui/src/app/services/dep-error.service.ts +++ b/web/projects/ui/src/app/services/dep-error.service.ts @@ -102,13 +102,20 @@ export class DepErrorService { } // invalid config - if (!currentDep.configSatisfied) { + if ( + Object.values(pkg.requestedActions).some( + a => + a.active && + a.request.packageId === depId && + a.request.actionId === 'config', + ) + ) { return { type: 'configUnsatisfied', } } - const depStatus = dep.status.main.status + const depStatus = dep.status.main // not running if (depStatus !== 'running' && depStatus !== 'starting') { @@ -120,7 +127,7 @@ export class DepErrorService { // health check failure if (depStatus === 'running' && currentDep.kind === 'running') { for (let id of currentDep.healthChecks) { - const check = dep.status.main.health[id] + const check = dep.status.health[id] if (check?.result !== 'success') { return { type: 'healthChecksFailed', diff --git a/web/projects/ui/src/app/services/form.service.ts b/web/projects/ui/src/app/services/form.service.ts index 779fb1f53..33fa29016 100644 --- a/web/projects/ui/src/app/services/form.service.ts +++ b/web/projects/ui/src/app/services/form.service.ts @@ -7,7 +7,7 @@ import { ValidatorFn, Validators, } from '@angular/forms' -import { CT, utils } from '@start9labs/start-sdk' +import { IST, utils } from '@start9labs/start-sdk' const Mustache = require('mustache') @Injectable({ @@ -17,16 +17,16 @@ export class FormService { constructor(private readonly formBuilder: UntypedFormBuilder) {} createForm( - spec: CT.InputSpec, + spec: IST.InputSpec, current: Record = {}, ): UntypedFormGroup { return this.getFormGroup(spec, [], current) } getUnionSelectSpec( - spec: CT.ValueSpecUnion, + spec: IST.ValueSpecUnion, selection: string | null, - ): CT.ValueSpecSelect { + ): IST.ValueSpecSelect { return { ...spec, type: 'select', @@ -38,7 +38,7 @@ export class FormService { } getUnionObject( - spec: CT.ValueSpecUnion, + spec: IST.ValueSpecUnion, selected: string | null, ): UntypedFormGroup { const group = this.getFormGroup({ @@ -53,16 +53,16 @@ export class FormService { return group } - getListItem(spec: CT.ValueSpecList, entry?: any) { - if (CT.isValueSpecListOf(spec, 'text')) { + getListItem(spec: IST.ValueSpecList, entry?: any) { + if (IST.isValueSpecListOf(spec, 'text')) { return this.formBuilder.control(entry, stringValidators(spec.spec)) - } else if (CT.isValueSpecListOf(spec, 'object')) { + } else if (IST.isValueSpecListOf(spec, 'object')) { return this.getFormGroup(spec.spec.spec, [], entry) } } getFormGroup( - config: CT.InputSpec, + config: IST.InputSpec, validators: ValidatorFn[] = [], current?: Record | null, ): UntypedFormGroup { @@ -77,7 +77,7 @@ export class FormService { } private getFormEntry( - spec: CT.ValueSpec, + spec: IST.ValueSpec, currentValue?: any, ): UntypedFormGroup | UntypedFormArray | UntypedFormControl { let value: any @@ -145,18 +145,18 @@ export class FormService { } } -// function getListItemValidators(spec: CT.ValueSpecList) { -// if (CT.isValueSpecListOf(spec, 'text')) { +// function getListItemValidators(spec: IST.ValueSpecList) { +// if (IST.isValueSpecListOf(spec, 'text')) { // return stringValidators(spec.spec) // } // } function stringValidators( - spec: CT.ValueSpecText | CT.ListValueSpecText, + spec: IST.ValueSpecText | IST.ListValueSpecText, ): ValidatorFn[] { const validators: ValidatorFn[] = [] - if ((spec as CT.ValueSpecText).required) { + if ((spec as IST.ValueSpecText).required) { validators.push(Validators.required) } @@ -169,7 +169,7 @@ function stringValidators( return validators } -function textareaValidators(spec: CT.ValueSpecTextarea): ValidatorFn[] { +function textareaValidators(spec: IST.ValueSpecTextarea): ValidatorFn[] { const validators: ValidatorFn[] = [] if (spec.required) { @@ -181,7 +181,7 @@ function textareaValidators(spec: CT.ValueSpecTextarea): ValidatorFn[] { return validators } -function colorValidators({ required }: CT.ValueSpecColor): ValidatorFn[] { +function colorValidators({ required }: IST.ValueSpecColor): ValidatorFn[] { const validators: ValidatorFn[] = [Validators.pattern(/^#[0-9a-f]{6}$/i)] if (required) { @@ -195,7 +195,7 @@ function datetimeValidators({ required, min, max, -}: CT.ValueSpecDatetime): ValidatorFn[] { +}: IST.ValueSpecDatetime): ValidatorFn[] { const validators: ValidatorFn[] = [] if (required) { @@ -213,12 +213,12 @@ function datetimeValidators({ return validators } -function numberValidators(spec: CT.ValueSpecNumber): ValidatorFn[] { +function numberValidators(spec: IST.ValueSpecNumber): ValidatorFn[] { const validators: ValidatorFn[] = [] validators.push(isNumber()) - if ((spec as CT.ValueSpecNumber).required) { + if ((spec as IST.ValueSpecNumber).required) { validators.push(Validators.required) } @@ -231,7 +231,7 @@ function numberValidators(spec: CT.ValueSpecNumber): ValidatorFn[] { return validators } -function selectValidators(spec: CT.ValueSpecSelect): ValidatorFn[] { +function selectValidators(spec: IST.ValueSpecSelect): ValidatorFn[] { const validators: ValidatorFn[] = [] if (spec.required) { @@ -241,13 +241,13 @@ function selectValidators(spec: CT.ValueSpecSelect): ValidatorFn[] { return validators } -function multiselectValidators(spec: CT.ValueSpecMultiselect): ValidatorFn[] { +function multiselectValidators(spec: IST.ValueSpecMultiselect): ValidatorFn[] { const validators: ValidatorFn[] = [] validators.push(listInRange(spec.minLength, spec.maxLength)) return validators } -function listValidators(spec: CT.ValueSpecList): ValidatorFn[] { +function listValidators(spec: IST.ValueSpecList): ValidatorFn[] { const validators: ValidatorFn[] = [] validators.push(listInRange(spec.minLength, spec.maxLength)) validators.push(listItemIssue()) @@ -352,7 +352,7 @@ export function listItemIssue(): ValidatorFn { } } -export function listUnique(spec: CT.ValueSpecList): ValidatorFn { +export function listUnique(spec: IST.ValueSpecList): ValidatorFn { return control => { const list = control.value for (let idx = 0; idx < list.length; idx++) { @@ -389,7 +389,11 @@ export function listUnique(spec: CT.ValueSpecList): ValidatorFn { } } -function listItemEquals(spec: CT.ValueSpecList, val1: any, val2: any): boolean { +function listItemEquals( + spec: IST.ValueSpecList, + val1: any, + val2: any, +): boolean { // TODO: fix types switch (spec.spec.type) { case 'text': @@ -402,7 +406,7 @@ function listItemEquals(spec: CT.ValueSpecList, val1: any, val2: any): boolean { } } -function itemEquals(spec: CT.ValueSpec, val1: any, val2: any): boolean { +function itemEquals(spec: IST.ValueSpec, val1: any, val2: any): boolean { switch (spec.type) { case 'text': case 'textarea': @@ -414,15 +418,15 @@ function itemEquals(spec: CT.ValueSpec, val1: any, val2: any): boolean { // TODO: 'unique-by' does not exist on ValueSpecObject, fix types return objEquals( (spec as any)['unique-by'], - spec as CT.ValueSpecObject, + spec as IST.ValueSpecObject, val1, val2, ) case 'union': - // TODO: 'unique-by' does not exist on CT.ValueSpecUnion, fix types + // TODO: 'unique-by' does not exist onIST.ValueSpecUnion, fix types return unionEquals( (spec as any)['unique-by'], - spec as CT.ValueSpecUnion, + spec as IST.ValueSpecUnion, val1, val2, ) @@ -442,8 +446,8 @@ function itemEquals(spec: CT.ValueSpec, val1: any, val2: any): boolean { } function listObjEquals( - uniqueBy: CT.UniqueBy, - spec: CT.ListValueSpecObject, + uniqueBy: IST.UniqueBy, + spec: IST.ListValueSpecObject, val1: any, val2: any, ): boolean { @@ -470,8 +474,8 @@ function listObjEquals( } function objEquals( - uniqueBy: CT.UniqueBy, - spec: CT.ValueSpecObject, + uniqueBy: IST.UniqueBy, + spec: IST.ValueSpecObject, val1: any, val2: any, ): boolean { @@ -499,8 +503,8 @@ function objEquals( } function unionEquals( - uniqueBy: CT.UniqueBy, - spec: CT.ValueSpecUnion, + uniqueBy: IST.UniqueBy, + spec: IST.ValueSpecUnion, val1: any, val2: any, ): boolean { @@ -532,8 +536,8 @@ function unionEquals( } function uniqueByMessageWrapper( - uniqueBy: CT.UniqueBy, - spec: CT.ListValueSpecObject, + uniqueBy: IST.UniqueBy, + spec: IST.ListValueSpecObject, ) { let configSpec = spec.spec @@ -544,8 +548,8 @@ function uniqueByMessageWrapper( } function uniqueByMessage( - uniqueBy: CT.UniqueBy, - configSpec: CT.InputSpec, + uniqueBy: IST.UniqueBy, + configSpec: IST.InputSpec, outermost = true, ): string { let joinFunc @@ -554,7 +558,7 @@ function uniqueByMessage( return '' } else if (typeof uniqueBy === 'string') { return configSpec[uniqueBy] - ? (configSpec[uniqueBy] as CT.ValueSpecObject).name + ? (configSpec[uniqueBy] as IST.ValueSpecObject).name : uniqueBy } else if ('any' in uniqueBy) { joinFunc = ' OR ' @@ -574,14 +578,14 @@ function uniqueByMessage( } function isObject( - spec: CT.ListValueSpecOf, -): spec is CT.ListValueSpecObject { + spec: IST.ListValueSpecOf, +): spec is IST.ListValueSpecObject { // only lists of objects have uniqueBy return 'uniqueBy' in spec } export function convertValuesRecursive( - configSpec: CT.InputSpec, + configSpec: IST.InputSpec, group: UntypedFormGroup, ) { Object.entries(configSpec).forEach(([key, valueSpec]) => { @@ -611,7 +615,7 @@ export function convertValuesRecursive( }) } else if (valueSpec.spec.type === 'object') { controls.forEach(formGroup => { - const objectSpec = valueSpec.spec as CT.ListValueSpecObject + const objectSpec = valueSpec.spec as IST.ListValueSpecObject convertValuesRecursive(objectSpec.spec, formGroup as UntypedFormGroup) }) } diff --git a/web/projects/ui/src/app/services/pkg-status-rendering.service.ts b/web/projects/ui/src/app/services/pkg-status-rendering.service.ts index d73a9afe1..f987f6b2c 100644 --- a/web/projects/ui/src/app/services/pkg-status-rendering.service.ts +++ b/web/projects/ui/src/app/services/pkg-status-rendering.service.ts @@ -1,6 +1,7 @@ import { PackageDataEntry } from 'src/app/services/patch-db/data-model' import { PkgDependencyErrors } from './dep-error.service' import { T } from '@start9labs/start-sdk' +import { getManifest, needsConfig } from '../util/get-package-data' export interface PackageStatus { primary: PrimaryStatus @@ -17,7 +18,7 @@ export function renderPkgStatus( let health: T.HealthStatus | null = null if (pkg.stateInfo.state === 'installed') { - primary = getInstalledPrimaryStatus(pkg.status) + primary = getInstalledPrimaryStatus(pkg) dependency = getDependencyStatus(depErrors) health = getHealthStatus(pkg.status) } else { @@ -27,11 +28,11 @@ export function renderPkgStatus( return { primary, dependency, health } } -function getInstalledPrimaryStatus(status: T.Status): PrimaryStatus { - if (!status.configured) { +function getInstalledPrimaryStatus(pkg: T.PackageDataEntry): PrimaryStatus { + if (needsConfig(getManifest(pkg).id, pkg.requestedActions)) { return 'needsConfig' } else { - return status.main.status as any as PrimaryStatus + return pkg.status.main } } @@ -39,12 +40,12 @@ function getDependencyStatus(depErrors: PkgDependencyErrors): DependencyStatus { return Object.values(depErrors).some(err => !!err) ? 'warning' : 'satisfied' } -function getHealthStatus(status: T.Status): T.HealthStatus | null { - if (status.main.status !== 'running' || !status.main.health) { +function getHealthStatus(status: T.MainStatus): T.HealthStatus | null { + if (status.main !== 'running' || !status.main) { return null } - const values = Object.values(status.main.health) + const values = Object.values(status.health) if (values.some(h => h.result === 'failure')) { return 'failure' diff --git a/web/projects/ui/src/app/util/configBuilderToSpec.ts b/web/projects/ui/src/app/util/configBuilderToSpec.ts index 1f75329c5..108bf9468 100644 --- a/web/projects/ui/src/app/util/configBuilderToSpec.ts +++ b/web/projects/ui/src/app/util/configBuilderToSpec.ts @@ -1,9 +1,9 @@ -import { CB } from '@start9labs/start-sdk' +import { ISB } from '@start9labs/start-sdk' export async function configBuilderToSpec( builder: - | CB.Config, unknown> - | CB.Config, never>, + | ISB.InputSpec, unknown> + | ISB.InputSpec, never>, ) { return builder.build({} as any) } diff --git a/web/projects/ui/src/app/util/get-package-data.ts b/web/projects/ui/src/app/util/get-package-data.ts index c4d0cc046..7ba7377fc 100644 --- a/web/projects/ui/src/app/util/get-package-data.ts +++ b/web/projects/ui/src/app/util/get-package-data.ts @@ -28,6 +28,18 @@ export function getManifest(pkg: PackageDataEntry): T.Manifest { return (pkg.stateInfo as InstallingState).installingInfo.newManifest } +export function needsConfig( + pkgId: string, + requestedActions: PackageDataEntry['requestedActions'], +): boolean { + return Object.values(requestedActions).some( + r => + r.active && + r.request.packageId === pkgId && + r.request.actionId === 'config', + ) +} + export function isInstalled( pkg: PackageDataEntry, ): pkg is PackageDataEntry {